INFRA-3126 : Cleanup of deprecated API's and dead code (#414)

* INFRA-3126 : Cleanup of deprecated API's and dead code

* INFRA-3126 : More cleanup
This commit is contained in:
Vijay Joshi
2024-04-15 17:28:39 +05:30
committed by GitHub
parent 0ed941abc3
commit 1f1679b272
35 changed files with 158 additions and 1781 deletions

View File

@@ -43,28 +43,6 @@ func NewIncidentHandler(
} }
} }
func (handler *IncidentHandler) HandleCreateIncident(c *gin.Context) {
var createIncidentRequest incident.CreateIncidentRequestV2
if err := c.ShouldBindJSON(&createIncidentRequest); err != nil {
c.JSON(http.StatusInternalServerError, err)
return
}
if err := utils.ValidateCreateIncidentRequestV2(createIncidentRequest); err != nil {
logger.Error(fmt.Sprintf("%s Received invalid request to create new incident", logTag), zap.Error(err))
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
return
}
incidentResponse, err := handler.service.CreateIncident(createIncidentRequest, "API", "")
if err != nil {
logger.Error(fmt.Sprintf("%s Failed to create incident", logTag), zap.Error(err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.CREATE_INCIDENT, err.Error())
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusInternalServerError, nil))
return
}
c.JSON(http.StatusOK, common.SuccessResponse(incidentResponse, http.StatusOK))
}
func (handler *IncidentHandler) HandleCreateIncidentV3(c *gin.Context) { func (handler *IncidentHandler) HandleCreateIncidentV3(c *gin.Context) {
var createIncidentRequest incident.CreateIncidentRequestV3 var createIncidentRequest incident.CreateIncidentRequestV3
if err := c.ShouldBindJSON(&createIncidentRequest); err != nil { if err := c.ShouldBindJSON(&createIncidentRequest); err != nil {

View File

@@ -29,7 +29,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
logger "houston/logger" "houston/logger"
) )
type slackHandler struct { type slackHandler struct {

View File

@@ -161,12 +161,9 @@ func (s *Server) incidentClientHandler(houstonGroup *gin.RouterGroup) {
origin := c.Request.Header.Get("Origin") origin := c.Request.Header.Get("Origin")
c.Writer.Header().Set("Access-Control-Allow-Origin", origin) c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
}) })
//Will be deprecated because they are not using hosuton group
s.gin.POST("/create-incident", incidentHandler.CreateIncident)
//TODO- Remove these api. Provide Multi Realm support in Internal Web BFF //TODO- Remove these api. Provide Multi Realm support in Internal Web BFF
houstonGroup.GET("/incidents/unsecured/v2/:id", incidentHandler.GetIncidents) houstonGroup.GET("/incidents/unsecured/v2/:id", incidentHandler.GetIncidents)
houstonGroup.POST("/create-incident", incidentHandler.CreateIncident)
} }
func (s *Server) productsHandler(houstonGroup *gin.RouterGroup) { func (s *Server) productsHandler(houstonGroup *gin.RouterGroup) {
@@ -216,7 +213,6 @@ func (s *Server) incidentClientHandlerV2(houstonGroup *gin.RouterGroup) {
incidentServiceV2, incidentServiceV2,
) )
incidentHandler := handler.NewIncidentHandler(s.gin, s.db, incidentServiceV2, incidentOrchestrator) incidentHandler := handler.NewIncidentHandler(s.gin, s.db, incidentServiceV2, incidentOrchestrator)
houstonGroup.POST("/create-incident-v2", incidentHandler.HandleCreateIncident)
houstonGroup.POST("/create-incident-v3", incidentHandler.HandleCreateIncidentV3) houstonGroup.POST("/create-incident-v3", incidentHandler.HandleCreateIncidentV3)
s.gin.POST("/incidents", authService.IfValidHoustonUser(incidentHandler.HandleUpdateIncident)) s.gin.POST("/incidents", authService.IfValidHoustonUser(incidentHandler.HandleUpdateIncident))

View File

@@ -28,28 +28,6 @@ func PublishHttpServerRequestMetrics(url string, method string, responseCode int
metrics.NewMetricPublisher().PublishMetrics(serverRequestMetric, ingester.HttpServerRequestMetrics) metrics.NewMetricPublisher().PublishMetrics(serverRequestMetric, ingester.HttpServerRequestMetrics)
} }
func PublishCronJobExecutionCounterMetrics(jobName string, duration float64) {
logger.Info("Publishing cron job execution counter metric")
cronJobMetric := ingester.MetricAttributes{
CronJobExecutionCounterMetric: ingester.CronJobExecutionCounterMetric{
JobName: jobName,
DurationInMs: duration,
},
}
metrics.NewMetricPublisher().PublishMetrics(cronJobMetric, ingester.CronJobExecutionCounterMetrics)
}
func PublishCronJobFailureMetrics(jobName string, errMessage string) {
logger.Info("Publishing cron job failure metric")
cronJobFailureMetric := ingester.MetricAttributes{
CronJobFailureMetric: ingester.CronJobFailureMetric{
JobName: jobName,
Error: errMessage,
},
}
metrics.NewMetricPublisher().PublishMetrics(cronJobFailureMetric, ingester.CronJobFailureMetrics)
}
func PublishHoustonFlowFailureMetrics(flowName string, errMessage string) { func PublishHoustonFlowFailureMetrics(flowName string, errMessage string) {
logger.Info("Publishing houston flow failure metric") logger.Info("Publishing houston flow failure metric")
flowFailureMetric := ingester.MetricAttributes{ flowFailureMetric := ingester.MetricAttributes{

View File

@@ -149,19 +149,6 @@ func GetColorBySeverity(severityId uint) string {
} }
} }
func GetColourByOpenIncidents(openIncidents int) string {
switch {
case openIncidents <= 5:
return "#50C878"
case openIncidents <= 10:
return "#FDDA0D"
case openIncidents > 10:
return "#FF0000"
default:
return "#808080"
}
}
func PostIncidentStatusUpdateMessage(userId, updatedStatus, channelId string, client *socketmode.Client) error { func PostIncidentStatusUpdateMessage(userId, updatedStatus, channelId string, client *socketmode.Client) error {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set status to %s", userId, updatedStatus), false) msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set status to %s", userId, updatedStatus), false)
_, _, errMessage := client.PostMessage(channelId, msgOption) _, _, errMessage := client.PostMessage(channelId, msgOption)
@@ -180,12 +167,6 @@ func PostIncidentTypeUpdateMessage(
return errMessage return errMessage
} }
func PostIncidentSeverityUpdateMessage(userId, updatedSeverity, channelId string, client *socketmode.Client) error {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set severity to %s", userId, updatedSeverity), false)
_, _, errMessage := client.PostMessage(channelId, msgOption)
return errMessage
}
func RemoveDuplicateStr(strSlice []string) []string { func RemoveDuplicateStr(strSlice []string) []string {
allKeys := make(map[string]bool) allKeys := make(map[string]bool)
list := []string{} list := []string{}

View File

@@ -209,13 +209,3 @@ func BuildSlackTextMessageFromMetaData(metadata []byte, isCodeBlock bool) (slack
return slack.MsgOptionText(textMessage, false), nil return slack.MsgOptionText(textMessage, false), nil
} }
func PostIncidentCustomerDataUpdateMessage(metadata *incidentRequest.CreateIncidentMetadata, userId, channelId string, client *socketmode.Client) error {
marshalledMetadata, _ := json.Marshal([]incidentRequest.CreateIncidentMetadata{*metadata})
msgOption, err := BuildSlackTextMessageFromMetaData(marshalledMetadata, true)
if err != nil {
return err
}
_, _, errMessage := client.PostMessage(channelId, msgOption)
return errMessage
}

View File

@@ -42,20 +42,6 @@ func (amp *PublisherImpl) PublishMetrics(metricAttributes ingester.MetricAttribu
} }
return return
} }
case ingester.CronJobExecutionCounterMetrics:
{
if err := publishCronJobExecutionCounterMetric(metricAttributes.CronJobExecutionCounterMetric); err != nil {
logger.Error("error while publishing cron job execution counter metrics", zap.Error(err))
}
return
}
case ingester.CronJobFailureMetrics:
{
if err := publishCronJobFailureMetric(metricAttributes.CronJobFailureMetric); err != nil {
logger.Error("error while publishing cron job failure metrics", zap.Error(err))
}
return
}
case ingester.HoustonFlowFailureMetrics: case ingester.HoustonFlowFailureMetrics:
{ {
if err := publishHoustonFlowFailureMetric(metricAttributes.HoustonFlowFailureMetric); err != nil { if err := publishHoustonFlowFailureMetric(metricAttributes.HoustonFlowFailureMetric); err != nil {
@@ -109,29 +95,6 @@ func publishHttpServerRequestMetric(serverRequestMetrics ingester.ServerRequestM
return return
} }
func publishCronJobExecutionCounterMetric(cronJobMetrics ingester.CronJobExecutionCounterMetric) (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
CronJobExecutionCounter.WithLabelValues(cronJobMetrics.JobName).Inc()
CronJobExecutionDurationSum.WithLabelValues(cronJobMetrics.JobName).Add(cronJobMetrics.DurationInMs)
CronJobExecutionDurationHistogram.WithLabelValues(cronJobMetrics.JobName).Observe(cronJobMetrics.DurationInMs)
CronJobExecutionDurationSummary.WithLabelValues(cronJobMetrics.JobName).Observe(cronJobMetrics.DurationInMs)
return
}
func publishCronJobFailureMetric(cronJobFailureMetrics ingester.CronJobFailureMetric) (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
CronJobFailureCounter.WithLabelValues(cronJobFailureMetrics.JobName, cronJobFailureMetrics.Error).Inc()
return
}
func publishHoustonFlowFailureMetric(houstonFlowFailureMetrics ingester.HoustonFlowFailureMetric) (err error) { func publishHoustonFlowFailureMetric(houstonFlowFailureMetrics ingester.HoustonFlowFailureMetric) (err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {

View File

@@ -90,48 +90,6 @@ var (
[]string{"url", "method", "response_code"}, []string{"url", "method", "response_code"},
) )
CronJobExecutionCounter = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_cron_job_execution_total",
Help: "Cron job execution counter",
},
[]string{"job_name"},
)
CronJobExecutionDurationSum = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_cron_job_execution_duration_sum",
Help: "Cron job execution duration sum",
},
[]string{"job_name"},
)
CronJobExecutionDurationHistogram = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "houston_cron_job_execution_duration_histogram",
Help: "Cron job execution duration histogram",
Buckets: cronExecutionDurationBuckets,
},
[]string{"job_name"},
)
CronJobExecutionDurationSummary = promauto.NewSummaryVec(
prometheus.SummaryOpts{
Name: "houston_cron_job_execution_duration_summary",
Help: "Cron job execution duration summary",
Objectives: quantileObjectives,
},
[]string{"job_name"},
)
CronJobFailureCounter = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "houston_cron_job_failure_total",
Help: "Cron job failure counter",
},
[]string{"job_name", "error"},
)
HoustonFlowFailureCounter = promauto.NewCounterVec( HoustonFlowFailureCounter = promauto.NewCounterVec(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "houston_flow_failure_total", Name: "houston_flow_failure_total",

View File

@@ -31,7 +31,7 @@ func NewIncidentGenerateRCAAction(client *socketmode.Client) *IncidentGenerateRC
func (igr *IncidentGenerateRCAAction) GenerateRCAProcess(callback slack.InteractionCallback, request *socketmode.Request) { func (igr *IncidentGenerateRCAAction) GenerateRCAProcess(callback slack.InteractionCallback, request *socketmode.Request) {
err := igr.slackService.UpdateMessageWithAttachments(callback.Channel.ID, callback.Message.Msg.Timestamp, view.GetRCAFailureAttachmentWithUpdatedMessage(callback.User.ID)) err := igr.slackService.UpdateMessageWithAttachments(callback.Channel.ID, callback.Message.Msg.Timestamp, view.GetRCAFailureAttachmentWithUpdatedMessage(callback.User.ID))
if err != nil { if err != nil {
logger.Error(fmt.Sprintf("Rca gneration error message update failed due to %s", err.Error())) logger.Error(fmt.Sprintf("Rca generation error message update failed due to %s", err.Error()))
} }
igr.client.Ack(*request) igr.client.Ack(*request)
incidentEntity, err := igr.incidentServiceV2.GetIncidentByChannelID(callback.Channel.ID) incidentEntity, err := igr.incidentServiceV2.GetIncidentByChannelID(callback.Channel.ID)

View File

@@ -1,24 +1,16 @@
package action package action
import ( import (
"errors"
"fmt"
"github.com/slack-go/slack" "github.com/slack-go/slack"
"github.com/spf13/viper"
"houston/appcontext" "houston/appcontext"
"houston/common/util" "houston/common/util"
"houston/internal/processor/action/view" "houston/internal/processor/action/view"
"houston/logger"
"houston/model/incident"
incidentJiraModel "houston/model/incident_jira" incidentJiraModel "houston/model/incident_jira"
incidentService "houston/service/incident"
"houston/service/incident_jira" "houston/service/incident_jira"
slack2 "houston/service/slack" slack2 "houston/service/slack"
"strings"
) )
type IncidentJiraLinksAction struct { type IncidentJiraLinksAction struct {
incidentService incidentService.IIncidentService
incidentJiraService incident_jira.IncidentJiraService incidentJiraService incident_jira.IncidentJiraService
slackService slack2.ISlackService slackService slack2.ISlackService
} }
@@ -28,11 +20,9 @@ const (
) )
func NewIncidentJiraLinksAction( func NewIncidentJiraLinksAction(
incidentService incidentService.IIncidentService,
slackService slack2.ISlackService, slackService slack2.ISlackService,
) *IncidentJiraLinksAction { ) *IncidentJiraLinksAction {
return &IncidentJiraLinksAction{ return &IncidentJiraLinksAction{
incidentService: incidentService,
incidentJiraService: incident_jira.NewIncidentJiraService(incidentJiraModel.NewIncidentJiraRepo(appcontext.GetDB())), incidentJiraService: incident_jira.NewIncidentJiraService(incidentJiraModel.NewIncidentJiraRepo(appcontext.GetDB())),
slackService: slackService, slackService: slackService,
} }
@@ -44,84 +34,6 @@ func (action *IncidentJiraLinksAction) getJiraLinksBlock(initialValue string) *s
return view.CreatePlainTextInputBlock(jiraLinksBlockData) return view.CreatePlainTextInputBlock(jiraLinksBlockData)
} }
// Deprecated: updateJiraLinks is deprecated. Use updateIncidentJiraLinks instead
func (action *IncidentJiraLinksAction) updateJiraLinks(jiraLinks string, callback slack.InteractionCallback, incidentEntity *incident.IncidentEntity) error {
channelID := callback.View.PrivateMetadata
formattedJiraLinks := strings.Split(
strings.ReplaceAll(strings.ReplaceAll(jiraLinks, "\n", ""), " ", ""),
",",
)
compareResults := util.CompareAndGetStringArrayResults(incidentEntity.JiraLinks, formattedJiraLinks)
//Update jira links only if there is a change. Validate by checking if there are any unique elements in either array
//If there are no unique elements in either array, then there is no change and skip the update
if !(len(compareResults.UniqueElementsInArrayA) == 0 && len(compareResults.UniqueElementsInArrayB) == 0) {
jiraLinksToBeUpdated := append(compareResults.CommonElements, compareResults.UniqueElementsInArrayB...)
//Below validation make sure that blank jira links are not to be tested for valid jira link
if len(jiraLinksToBeUpdated) > 0 && jiraLinksToBeUpdated[0] != "" {
for _, link := range jiraLinksToBeUpdated {
//Validate jira link
if !strings.HasPrefix(link, viper.GetString("navi.jira.base.url")) {
err := action.slackService.PostEphemeralByChannelID(fmt.Sprintf("%s is not a valid jira link", link), callback.User.ID, false, channelID)
if err != nil {
logger.Error(fmt.Sprintf("%s failed to post jira link validation failure ephemeral to slack channel: %s", logTag, incidentEntity.IncidentName))
return err
}
return errors.New(fmt.Sprintf("%s is a invalid jira link", link))
}
}
}
err := action.incidentService.UpdateIncidentJiraLinksEntity(incidentEntity, callback.User.ID, jiraLinksToBeUpdated)
if err != nil {
logger.Error(fmt.Sprintf("%s unable to update jira link(s) for incident %s", logTag, incidentEntity.IncidentName))
return err
}
}
return nil
}
func (action *IncidentJiraLinksAction) updateIncidentJiraLinks(jiraLinks string, incidentEntity *incident.IncidentEntity) error {
err := action.incidentJiraService.RemoveAllJiraLinksByIncidentID(incidentEntity.ID)
if err != nil {
logger.Error(fmt.Sprintf("%s unable to remove jira links for incident %s", logTag, incidentEntity.IncidentName))
return err
}
validatedJiraLinksPtr, err := action.getValidatedJiraLinks(jiraLinks)
if err != nil {
return err
}
if validatedJiraLinksPtr != nil && len(*validatedJiraLinksPtr) > 0 {
_, err = action.incidentJiraService.AddJiraLinksByIncidentID(incidentEntity.ID, *validatedJiraLinksPtr)
if err != nil {
logger.Error(fmt.Sprintf("%s unable to add jira link(s) for incident %s", logTag, incidentEntity.IncidentName))
return err
}
}
return nil
}
func (action *IncidentJiraLinksAction) GetIncidentJiraLinks(incidentID uint) (*[]incidentJiraModel.IncidentJiraEntity, error) { func (action *IncidentJiraLinksAction) GetIncidentJiraLinks(incidentID uint) (*[]incidentJiraModel.IncidentJiraEntity, error) {
return action.incidentJiraService.GetJiraLinksByIncidentID(incidentID) return action.incidentJiraService.GetJiraLinksByIncidentID(incidentID)
} }
func (action *IncidentJiraLinksAction) getValidatedJiraLinks(jiraLinks string) (*[]string, error) {
var formattedJiraLinks []string
if jiraLinks != "" {
formattedJiraLinks = strings.Split(
strings.ReplaceAll(strings.ReplaceAll(jiraLinks, "\n", ""), " ", ""),
",",
)
}
if len(formattedJiraLinks) > 0 && formattedJiraLinks != nil {
for _, link := range formattedJiraLinks {
//Validate jira link
if !strings.HasPrefix(link, viper.GetString("navi.jira.base.url")) {
return nil, errors.New(fmt.Sprintf("%s is not a valid Jira link", link))
}
}
}
return &formattedJiraLinks, nil
}

View File

@@ -1,84 +0,0 @@
package action
import (
"errors"
"github.com/gojuno/minimock/v3"
"github.com/slack-go/slack"
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
"houston/logger"
"houston/mocks"
"strings"
"testing"
)
func getJiraLinks() []string {
return []string{"https://navihq.atlassian.net/browse/TP-44155", "https://navihq.atlassian.net/browse/TP-44157"}
}
type IncidentJiraLinksActionSuite struct {
suite.Suite
}
func TestIncidentJiraLinksAction(t *testing.T) {
suite.Run(t, new(IncidentJiraLinksActionSuite))
}
func (suite *IncidentJiraLinksActionSuite) SetupSuite() {
logger.InitLogger()
viper.Set("navi.jira.base.url", "https://navihq.atlassian.net/browse/")
}
func (suite *IncidentJiraLinksActionSuite) TestUpdateJiraLinksFailureAtUpdatingEntity() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentService := mocks.NewIIncidentServiceMock(controller)
slackService := mocks.NewISlackServiceMock(controller)
incidentService.UpdateIncidentJiraLinksEntityMock.Return(errors.New("failure while updating jira links"))
jiraLinksActions := NewIncidentJiraLinksAction(incidentService, slackService)
err := jiraLinksActions.updateJiraLinks(strings.Join(getJiraLinks(), ", "), slack.InteractionCallback{}, getMockIncidentEntity())
suite.EqualError(err, "failure while updating jira links")
}
func (suite *IncidentJiraLinksActionSuite) TestUpdateJiraLinksFailureForInvalidJiraLink() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentService := mocks.NewIIncidentServiceMock(controller)
slackService := mocks.NewISlackServiceMock(controller)
slackService.PostEphemeralByChannelIDMock.Return(nil)
jiraLinksActions := NewIncidentJiraLinksAction(incidentService, slackService)
err := jiraLinksActions.updateJiraLinks("dsa", slack.InteractionCallback{}, getMockIncidentEntity())
suite.EqualError(err, "dsa is a invalid jira link")
}
func (suite *IncidentJiraLinksActionSuite) TestUpdateJiraLinksSuccessBlankValue() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentService := mocks.NewIIncidentServiceMock(controller)
slackService := mocks.NewISlackServiceMock(controller)
incidentService.UpdateIncidentJiraLinksEntityMock.Return(nil)
jiraLinksActions := NewIncidentJiraLinksAction(incidentService, slackService)
err := jiraLinksActions.updateJiraLinks("", slack.InteractionCallback{}, getMockIncidentEntity())
suite.NoError(err)
}
func (suite *IncidentJiraLinksActionSuite) TestUpdateJiraLinksSuccessDiffValue() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentService := mocks.NewIIncidentServiceMock(controller)
slackService := mocks.NewISlackServiceMock(controller)
incidentService.UpdateIncidentJiraLinksEntityMock.Return(nil)
jiraLinksActions := NewIncidentJiraLinksAction(incidentService, slackService)
err := jiraLinksActions.updateJiraLinks(strings.Join(getJiraLinks(), ", "), slack.InteractionCallback{}, getMockIncidentEntity())
suite.NoError(err)
}
func (suite *IncidentJiraLinksActionSuite) TestUpdateJiraLinksSuccessSameValue() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentService := mocks.NewIIncidentServiceMock(controller)
slackService := mocks.NewISlackServiceMock(controller)
jiraLinksActions := NewIncidentJiraLinksAction(incidentService, slackService)
err := jiraLinksActions.updateJiraLinks(strings.Join(getMockIncidentEntity().JiraLinks, ", "), slack.InteractionCallback{}, getMockIncidentEntity())
suite.NoError(err)
}

View File

@@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/slack-go/slack" "github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"houston/common/jira" "houston/common/jira"
"houston/common/metrics" "houston/common/metrics"
@@ -108,55 +107,22 @@ func (action *IncidentRCASectionAction) PerformSetIncidentRCADetailsAction(
var payload interface{} var payload interface{}
action.client.Ack(*request, payload) action.client.Ack(*request, payload)
if viper.GetBool("RESOLVE_INCIDENT_SLACK_REFACTOR_ENABLED") { resolveIncidentRequest, err := service.ConvertSlackActionToResolveIncidentRequest(actions, incidentEntity.ID)
request, err := service.ConvertSlackActionToResolveIncidentRequest(actions, incidentEntity.ID) if err != nil {
if err != nil { logger.Error(
logger.Error( fmt.Sprintf("Error while creating resolve request for incident id: %d", incidentEntity.ID),
fmt.Sprintf("Error while creating resolve request for incident id: %d", incidentEntity.ID), zap.Error(err),
zap.Error(err),
)
return
}
err = action.incidentService.ExecuteIncidentResolveFlow(
*request, incidentEntity, callback.User.ID, string(requesterType),
) )
if err != nil {
metrics.PublishHoustonFlowFailureMetrics(incidentServiceImpl.RESOLVE_INCIDENT, err.Error())
logger.Error(fmt.Sprintf("Error while resolving incident with id: %d", incidentEntity.ID), zap.Error(err))
}
return return
} }
err = action.tagsAction.updateTags(actions, callback, incidentEntity) err = action.incidentService.ExecuteIncidentResolveFlow(
*resolveIncidentRequest, incidentEntity, callback.User.ID, string(requesterType),
)
if err != nil { if err != nil {
logger.Error(fmt.Sprintf("failed to update the incicent tags for incident id: %v", incidentEntity.ID)) metrics.PublishHoustonFlowFailureMetrics(incidentServiceImpl.RESOLVE_INCIDENT, err.Error())
logger.Error(fmt.Sprintf("Error while resolving incident with id: %d", incidentEntity.ID), zap.Error(err))
} }
rcaValue := actions[util.SetRCASummary].Value
err = action.rcaSummaryAction.updateRCASummary(rcaValue, incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("failed to update rca summary for incident id: %v", incidentEntity.ID))
}
jiraLinksValue := actions[util.SetJiraLinks].Value
err = action.jiraAction.updateIncidentJiraLinks(jiraLinksValue, incidentEntity)
if err != nil {
_, _ = action.client.PostEphemeral(
callback.View.PrivateMetadata,
callback.User.ID,
slack.MsgOptionText(fmt.Sprintf("Failed to update Jira link(s). %s", err.Error()), false),
)
logger.Error(fmt.Sprintf("failed to update Jira link(s) for incident id: %v. %+v", incidentEntity.ID, err))
} else {
//todo: this is to be removed after jira_links column is removed from incident table
err = action.jiraAction.updateJiraLinks(jiraLinksValue, callback, incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("failed to update Jira link(s) for incident id: %v", incidentEntity.ID))
}
}
updatedIncidentEntity, _ := action.incidentRepository.FindIncidentById(incidentEntity.ID)
tagValuesMap, _ := action.tagsAction.getIncidentTagValuesAsMap(incidentEntity.ID)
action.postRCADetailsBlock(updatedIncidentEntity, tagValuesMap)
action.performPostUpdateActions(requesterType, callback, request)
} }
func (action *IncidentRCASectionAction) PerformShowRCADetailsAction(callback slack.InteractionCallback, request *socketmode.Request) { func (action *IncidentRCASectionAction) PerformShowRCADetailsAction(callback slack.InteractionCallback, request *socketmode.Request) {
@@ -171,15 +137,6 @@ func (action *IncidentRCASectionAction) PerformShowRCADetailsAction(callback sla
action.postRCADetailsBlock(incidentEntity, tagValuesMap) action.postRCADetailsBlock(incidentEntity, tagValuesMap)
} }
func (action *IncidentRCASectionAction) performPostUpdateActions(requesterType util.ViewSubmissionType, callback slack.InteractionCallback, request *socketmode.Request) {
switch requesterType {
case util.IncidentResolveSubmit:
resolveAction := NewIncidentResolveProcessor(action.client, action.incidentRepository, action.tagRepository, action.teamRepository, action.severityRepository, action.rcaService)
resolveAction.IncidentResolveProcess(callback, request)
}
}
func (action *IncidentRCASectionAction) postRCADetailsBlock(entity *incident.IncidentEntity, tagValueMaps map[string][]string) { func (action *IncidentRCASectionAction) postRCADetailsBlock(entity *incident.IncidentEntity, tagValueMaps map[string][]string) {
blocks := view.ConstructShowRCADetailsBlock(entity, tagValueMaps) blocks := view.ConstructShowRCADetailsBlock(entity, tagValueMaps)
color := util.GetColorBySeverity(4) color := util.GetColorBySeverity(4)

View File

@@ -1,23 +1,16 @@
package action package action
import ( import (
"fmt"
"github.com/slack-go/slack" "github.com/slack-go/slack"
"houston/common/util" "houston/common/util"
"houston/internal/processor/action/view" "houston/internal/processor/action/view"
"houston/logger"
"houston/model/incident"
"strings"
) )
type IncidentRCASummaryAction struct { type IncidentRCASummaryAction struct {
incidentRepository incident.IIncidentRepository
} }
func NewIncidentRCASummaryAction(incidentService incident.IIncidentRepository) *IncidentRCASummaryAction { func NewIncidentRCASummaryAction() *IncidentRCASummaryAction {
return &IncidentRCASummaryAction{ return &IncidentRCASummaryAction{}
incidentRepository: incidentService,
}
} }
func (action *IncidentRCASummaryAction) getRCASummaryBlock(rcaInitialValue string) *slack.InputBlock { func (action *IncidentRCASummaryAction) getRCASummaryBlock(rcaInitialValue string) *slack.InputBlock {
@@ -25,19 +18,3 @@ func (action *IncidentRCASummaryAction) getRCASummaryBlock(rcaInitialValue strin
rcaBlockData := view.SimpleInputBlockElementData{BasicData: rcaBlockBasicData, InitialValue: rcaInitialValue} rcaBlockData := view.SimpleInputBlockElementData{BasicData: rcaBlockBasicData, InitialValue: rcaInitialValue}
return view.CreatePlainTextInputBlock(rcaBlockData) return view.CreatePlainTextInputBlock(rcaBlockData)
} }
func (action *IncidentRCASummaryAction) updateRCASummary(rcaValue string, incidentEntity *incident.IncidentEntity) error {
trimmedRCA := strings.TrimSpace(rcaValue)
//Update RCA only if provided RCA is nt blank and different from existing
if trimmedRCA != incidentEntity.RCA {
incidentEntity.RCA = trimmedRCA
err := action.incidentRepository.UpdateIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("IncidentUpdateRca error for incident %s : %s", incidentEntity.IncidentName, err.Error()))
return err
}
} else {
logger.Info(fmt.Sprintf("RCA is not changed for incident %s", incidentEntity.IncidentName))
}
return nil
}

View File

@@ -1,56 +0,0 @@
package action
import (
"errors"
"github.com/gojuno/minimock/v3"
"github.com/stretchr/testify/suite"
"houston/logger"
"houston/mocks"
"houston/model/incident"
"testing"
)
func getMockIncidentEntity() *incident.IncidentEntity {
return &incident.IncidentEntity{IncidentName: "test", RCA: "test", JiraLinks: []string{"https://navihq.atlassian.net/browse/TP-44157", "https://navihq.atlassian.net/browse/TP-44158"}}
}
type IncidentRCASummaryActionSuite struct {
suite.Suite
}
func TestIncidentRCASummaryActions(t *testing.T) {
suite.Run(t, new(IncidentRCASummaryActionSuite))
}
func (suite *IncidentRCASummaryActionSuite) SetupSuite() {
logger.InitLogger()
}
func (suite *IncidentRCASummaryActionSuite) TestUpdateRCASummaryFailure() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentRepo := mocks.NewIIncidentRepositoryMock(controller)
incidentRepo.UpdateIncidentMock.Return(errors.New("failure while updating incident"))
rcaSummaryActions := NewIncidentRCASummaryAction(incidentRepo)
err := rcaSummaryActions.updateRCASummary("", getMockIncidentEntity())
suite.EqualError(err, "failure while updating incident")
}
func (suite *IncidentRCASummaryActionSuite) TestUpdateRCASummarySuccessForSameRCAValue() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentRepo := mocks.NewIIncidentRepositoryMock(controller)
rcaSummaryActions := NewIncidentRCASummaryAction(incidentRepo)
err := rcaSummaryActions.updateRCASummary("test", getMockIncidentEntity())
suite.NoError(err)
}
func (suite *IncidentRCASummaryActionSuite) TestUpdateRCASummarySuccess() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
incidentRepo := mocks.NewIIncidentRepositoryMock(controller)
incidentRepo.UpdateIncidentMock.Return(nil)
rcaSummaryActions := NewIncidentRCASummaryAction(incidentRepo)
err := rcaSummaryActions.updateRCASummary("test1", getMockIncidentEntity())
suite.NoError(err)
}

View File

@@ -4,10 +4,8 @@ import (
"fmt" "fmt"
"github.com/slack-go/slack" "github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"houston/common/metrics" "houston/common/metrics"
incidentHelper "houston/common/util"
"houston/common/util/structUtil" "houston/common/util/structUtil"
"houston/internal/processor/action/view" "houston/internal/processor/action/view"
"houston/logger" "houston/logger"
@@ -18,10 +16,9 @@ import (
incidentService "houston/service/incident/impl" incidentService "houston/service/incident/impl"
service "houston/service/request" service "houston/service/request"
"strconv" "strconv"
"time"
) )
type IncidentUpdateSevertityAction struct { type IncidentUpdateSeverityAction struct {
client *socketmode.Client client *socketmode.Client
severityRepository *severity.Repository severityRepository *severity.Repository
incidentRepository *incident.Repository incidentRepository *incident.Repository
@@ -37,8 +34,8 @@ func NewIncidentUpdateSeverityAction(
teamRepository *team.Repository, teamRepository *team.Repository,
slackbotClient *slackbot.Client, slackbotClient *slackbot.Client,
incidentServiceV2 *incidentService.IncidentServiceV2, incidentServiceV2 *incidentService.IncidentServiceV2,
) *IncidentUpdateSevertityAction { ) *IncidentUpdateSeverityAction {
return &IncidentUpdateSevertityAction{ return &IncidentUpdateSeverityAction{
client: client, client: client,
severityRepository: severityRepository, severityRepository: severityRepository,
incidentRepository: incidentRepository, incidentRepository: incidentRepository,
@@ -53,7 +50,7 @@ type JustificationMetadata struct {
ChannelId string ChannelId string
} }
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityRequestProcess(callback slack.InteractionCallback, request *socketmode.Request) { func (isp *IncidentUpdateSeverityAction) IncidentUpdateSeverityRequestProcess(callback slack.InteractionCallback, request *socketmode.Request) {
incidentSeverity, err := isp.severityRepository.GetAllActiveSeverity() incidentSeverity, err := isp.severityRepository.GetAllActiveSeverity()
if err != nil || incidentSeverity == nil { if err != nil || incidentSeverity == nil {
logger.Error("FindSeverityEntity error", logger.Error("FindSeverityEntity error",
@@ -65,7 +62,7 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityRequestProcess(c
_, err = isp.client.OpenView(callback.TriggerID, modalRequest) _, err = isp.client.OpenView(callback.TriggerID, modalRequest)
if err != nil { if err != nil {
logger.Error("houston slackbot openview command failed.", logger.Error("houston slackbot open view command failed.",
zap.String("trigger_id", callback.TriggerID), zap.String("channel_id", callback.Channel.ID), zap.Error(err)) zap.String("trigger_id", callback.TriggerID), zap.String("channel_id", callback.Channel.ID), zap.Error(err))
return return
} }
@@ -73,7 +70,7 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityRequestProcess(c
isp.client.Ack(*request, payload) isp.client.Ack(*request, payload)
} }
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityJustification(callback slack.InteractionCallback, request *socketmode.Request, user slack.User) { func (isp *IncidentUpdateSeverityAction) IncidentUpdateSeverityJustification(callback slack.InteractionCallback, request *socketmode.Request, user slack.User) {
var justificationMetadata JustificationMetadata var justificationMetadata JustificationMetadata
err := structUtil.StringToStruct(callback.View.PrivateMetadata, &justificationMetadata) err := structUtil.StringToStruct(callback.View.PrivateMetadata, &justificationMetadata)
if err != nil { if err != nil {
@@ -115,10 +112,9 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityJustification(ca
logger.Error(fmt.Sprintf("error in updating severity: %v", err)) logger.Error(fmt.Sprintf("error in updating severity: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error()) metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
} }
return
} }
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverity(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) { func (isp *IncidentUpdateSeverityAction) IncidentUpdateSeverity(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) {
channelId := callback.View.PrivateMetadata channelId := callback.View.PrivateMetadata
incidentEntity, err := isp.incidentRepository.FindIncidentByChannelId(channelId) incidentEntity, err := isp.incidentRepository.FindIncidentByChannelId(channelId)
if err != nil { if err != nil {
@@ -135,134 +131,44 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverity(callback slack.
incidentSeverityId := buildUpdateIncidentSeverityRequest(callback.View.State.Values) incidentSeverityId := buildUpdateIncidentSeverityRequest(callback.View.State.Values)
if viper.GetBool("UPDATE_INCIDENT_V2_ENABLED") {
var payload interface{}
isp.client.Ack(*request, payload)
teamEntity, _, incidentStatusEntity, incidentChannels, err := isp.incidentServiceV2.FetchAllEntitiesForIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err))
return
}
if viper.GetBool("ENABLE_DE_ESCALATION_JUSTIFICATION") {
if uint(incidentSeverityId) > incidentEntity.SeverityId {
justificationMetadata := JustificationMetadata{SeverityId: uint(incidentSeverityId), ChannelId: channelId}
stringData, stringErr := structUtil.StructToString(justificationMetadata)
if stringErr != nil {
logger.Error(fmt.Sprintf("error in converting justification metadata to string for incident id: %d", incidentEntity.ID), zap.Error(stringErr))
}
_, viewErr := isp.client.OpenView(callback.TriggerID, view.CreateSeverityJustificationBlock(stringData))
if viewErr != nil {
logger.Error(fmt.Sprintf("error in opening justification view for incident with id: %d", incidentEntity.ID), zap.Error(viewErr))
}
return
}
}
if err := isp.incidentServiceV2.UpdateSeverityId(
service.UpdateIncidentRequest{
Id: incidentEntity.ID,
SeverityId: strconv.Itoa(incidentSeverityId),
MetaData: nil,
},
user.ID,
incidentEntity,
teamEntity,
incidentStatusEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
}
return
}
incidentSeverityEntity, err := isp.severityRepository.FindIncidentSeverityEntityById(incidentSeverityId)
if err != nil {
logger.Error("FindIncidentSeverityEntityById error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
} else if incidentSeverityEntity == nil {
logger.Error("SeverityEntity not found",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
}
incidentEntity.SeverityId = incidentSeverityEntity.ID
incidentEntity.UpdatedBy = user.ID
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, incidentSeverityEntity.Sla)
err = isp.incidentRepository.UpdateIncident(incidentEntity)
if err != nil {
logger.Error("UpdateIncident error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
}
teamEntity, err := isp.teamRepository.FindTeamById(incidentEntity.TeamId)
for _, o := range incidentSeverityEntity.SlackUserIds {
isp.slackbotClient.InviteUsersToConversation(callback.View.PrivateMetadata, o)
}
go func() {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `set severity to %s (%s)`", user.ID, incidentSeverityEntity.Name, incidentSeverityEntity.Description), false)
_, _, errMessage := isp.client.PostMessage(callback.View.PrivateMetadata, msgOption)
if errMessage != nil {
logger.Error("post response failed for IncidentUpdateSeverity", zap.Error(errMessage))
return
}
if err != nil {
logger.Error("FindTeamEntityById error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
} else if teamEntity == nil {
logger.Error("Team Not Found",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
}
txt := fmt.Sprintf("set the channel topic: *%s · %s (%s) %s* | %s", teamEntity.Name, incidentSeverityEntity.Name, incidentSeverityEntity.Description, incidentEntity.IncidentName, incidentEntity.Title)
att := slack.Attachment{
Text: txt,
Color: "#808080", // Grey color code
MarkdownIn: []string{"txt"}, // Define which fields support markdown
}
_, _, errMessage = isp.client.PostMessage(callback.View.PrivateMetadata, slack.MsgOptionAttachments(att))
if errMessage != nil {
logger.Error("post response failed for IncidentUpdateType", zap.Error(errMessage))
return
}
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, incidentSeverityEntity.Name, incidentSeverityEntity.Description, incidentEntity.ID, incidentEntity.Title)
isp.slackbotClient.SetChannelTopic(callback.View.PrivateMetadata, topic)
}()
incidentHelper.TagPseOrDevOncallToIncident(
callback.View.PrivateMetadata,
incidentSeverityEntity,
teamEntity,
isp.slackbotClient,
isp.client,
)
err = incidentHelper.AssignResponderToIncident(
isp.incidentRepository,
incidentEntity,
teamEntity,
incidentSeverityEntity,
isp.client,
user.ID,
)
if err != nil {
logger.Error("[Update incident severity] Error while assigning responder to the incident ", zap.Error(err))
}
var payload interface{} var payload interface{}
isp.client.Ack(*request, payload) isp.client.Ack(*request, payload)
teamEntity, _, incidentStatusEntity, incidentChannels, err := isp.incidentServiceV2.FetchAllEntitiesForIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err))
return
}
if uint(incidentSeverityId) > incidentEntity.SeverityId {
justificationMetadata := JustificationMetadata{SeverityId: uint(incidentSeverityId), ChannelId: channelId}
stringData, stringErr := structUtil.StructToString(justificationMetadata)
if stringErr != nil {
logger.Error(fmt.Sprintf("error in converting justification metadata to string for incident id: %d", incidentEntity.ID), zap.Error(stringErr))
}
_, viewErr := isp.client.OpenView(callback.TriggerID, view.CreateSeverityJustificationBlock(stringData))
if viewErr != nil {
logger.Error(fmt.Sprintf("error in opening justification view for incident with id: %d", incidentEntity.ID), zap.Error(viewErr))
}
return
}
if err := isp.incidentServiceV2.UpdateSeverityId(
service.UpdateIncidentRequest{
Id: incidentEntity.ID,
SeverityId: strconv.Itoa(incidentSeverityId),
MetaData: nil,
},
user.ID,
incidentEntity,
teamEntity,
incidentStatusEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
}
} }
// TODO - ADD USER ACCORDING TO SEVERITY
func buildUpdateIncidentSeverityRequest(blockActions map[string]map[string]slack.BlockAction) int { func buildUpdateIncidentSeverityRequest(blockActions map[string]map[string]slack.BlockAction) int {
var requestMap = make(map[string]string, 0) var requestMap = make(map[string]string, 0)
for _, actions := range blockActions { for _, actions := range blockActions {
@@ -276,7 +182,7 @@ func buildUpdateIncidentSeverityRequest(blockActions map[string]map[string]slack
selectedValue := requestMap["incident_severity_modal_request"] selectedValue := requestMap["incident_severity_modal_request"]
selectedValueInInt, err := strconv.Atoi(selectedValue) selectedValueInInt, err := strconv.Atoi(selectedValue)
if err != nil { if err != nil {
logger.Error("String conversion to int faileed in buildUpdateIncidentResponderTeamRequest for "+selectedValue, zap.Error(err)) logger.Error("String conversion to int failed in buildUpdateIncidentResponderTeamRequest for "+selectedValue, zap.Error(err))
} }
return selectedValueInInt return selectedValueInInt
} }

View File

@@ -5,17 +5,15 @@ import (
"github.com/slack-go/slack" "github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"go.uber.org/zap" "go.uber.org/zap"
"houston/common/util" "houston/common/metrics"
houstonSlackUtil "houston/common/util/slack"
"houston/internal/processor/action/view" "houston/internal/processor/action/view"
"houston/logger" "houston/logger"
"houston/model/incident" "houston/model/incident"
"houston/model/severity" "houston/model/severity"
"houston/model/tag" "houston/model/tag"
"houston/model/team" "houston/model/team"
"math" incidentV2 "houston/service/incident/impl"
"strconv" service "houston/service/request"
"time"
) )
type UpdateIncidentAction struct { type UpdateIncidentAction struct {
@@ -24,15 +22,23 @@ type UpdateIncidentAction struct {
tagService *tag.Repository tagService *tag.Repository
teamRepository *team.Repository teamRepository *team.Repository
severityRepository *severity.Repository severityRepository *severity.Repository
incidentServiceV2 *incidentV2.IncidentServiceV2
} }
func NewIncidentUpdateAction(client *socketmode.Client, incidentService *incident.Repository, tagService *tag.Repository, teamRepository *team.Repository, severityRepository *severity.Repository) *UpdateIncidentAction { func NewIncidentUpdateAction(
client *socketmode.Client,
incidentService *incident.Repository,
tagService *tag.Repository,
teamRepository *team.Repository,
severityRepository *severity.Repository,
incidentServiceV2 *incidentV2.IncidentServiceV2) *UpdateIncidentAction {
return &UpdateIncidentAction{ return &UpdateIncidentAction{
client: client, client: client,
incidentService: incidentService, incidentService: incidentService,
tagService: tagService, tagService: tagService,
teamRepository: teamRepository, teamRepository: teamRepository,
severityRepository: severityRepository, severityRepository: severityRepository,
incidentServiceV2: incidentServiceV2,
} }
} }
@@ -55,7 +61,6 @@ func (isp *UpdateIncidentAction) IncidentUpdateStatusRequestProcess(callback sla
isp.client.Ack(*request, payload) isp.client.Ack(*request, payload)
} }
// todo: this method has to be replaced with incident-service-v2 UpdateStatus method
func (isp *UpdateIncidentAction) IncidentUpdateStatus(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) { func (isp *UpdateIncidentAction) IncidentUpdateStatus(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) {
incidentEntity, err := isp.incidentService.FindIncidentByChannelId(callback.View.PrivateMetadata) incidentEntity, err := isp.incidentService.FindIncidentByChannelId(callback.View.PrivateMetadata)
if err != nil { if err != nil {
@@ -68,77 +73,37 @@ func (isp *UpdateIncidentAction) IncidentUpdateStatus(callback slack.Interaction
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name), zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err)) zap.String("user_id", user.ID), zap.Error(err))
return return
} }
incidentStatusId := buildUpdateIncidentStatusRequest(callback.View.State.Values) incidentStatusId := buildUpdateIncidentStatusRequest(callback.View.State.Values)
result, err := isp.incidentService.FindIncidentStatusById(incidentStatusId)
teamEntity, severityEntity, _, incidentChannels, err := isp.incidentServiceV2.FetchAllEntitiesForIncident(incidentEntity)
if err != nil { if err != nil {
logger.Error("FindIncidentStatusById error", logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err))
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
} else if result == nil {
logger.Error("IncidentStatusEntity Object not found",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return return
} }
var payload interface{} var payload interface{}
isp.client.Ack(*request, payload) isp.client.Ack(*request, payload)
channelId := callback.View.PrivateMetadata if err := isp.incidentServiceV2.UpdateStatus(
if incidentEntity.Status == result.ID { service.UpdateIncidentRequest{
houstonSlackUtil.PostIncidentStatusErrorMessage(result.Name, channelId, callback.User.ID, isp.client) Id: incidentEntity.ID,
Status: incidentStatusId,
},
user.ID,
callback.View.PrivateMetadata,
incidentEntity,
teamEntity,
severityEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating status: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentV2.UPDATE_INCIDENT_STATUS, err.Error())
return return
} }
currentStatus, _ := isp.incidentService.FindIncidentStatusById(incidentEntity.Status)
incidentEntity.Status = result.ID
incidentEntity.UpdatedBy = user.ID
err = isp.incidentService.UpdateIncident(incidentEntity)
if err != nil {
logger.Error("UpdateIncident error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
}
go func() {
errMessage := util.PostIncidentStatusUpdateMessage(user.ID, result.Name, callback.View.PrivateMetadata, isp.client)
if errMessage != nil {
logger.Error("post response failed for IncidentUpdateStatus", zap.Error(errMessage))
return
}
if currentStatus.IsTerminalStatus && incidentEntity.SeverityId != incident.Sev0Id {
toSeverity, _ := isp.severityRepository.FindSeverityById(incidentEntity.SeverityId - 1)
var message string
if (incidentEntity.SeverityTat).Before(time.Now()) {
message = fmt.Sprintf(
"Severity TAT has already breached, this incident will be auto-escalated to `%v`",
toSeverity.Name,
)
} else if (incidentEntity.SeverityTat).Before(time.Now().AddDate(0, 0, 1)) {
message = fmt.Sprintf(
"Severity TAT is breaching in %d hours, this incident will be auto-escalated to `%s`",
int(math.Ceil(incidentEntity.SeverityTat.Sub(time.Now()).Hours())), toSeverity.Name,
)
}
if message != "" {
_, _, err := isp.client.PostMessage(channelId, slack.MsgOptionText(message, false))
if err != nil {
logger.Error(fmt.Sprintf("%s error in posting escalation message", logTag), zap.Error(err))
}
}
}
msgUpdate := NewIncidentChannelMessageUpdateAction(isp.client, isp.incidentService, isp.teamRepository, isp.severityRepository)
msgUpdate.ProcessAction(incidentEntity.SlackChannel)
}()
} }
func buildUpdateIncidentStatusRequest(blockActions map[string]map[string]slack.BlockAction) uint { func buildUpdateIncidentStatusRequest(blockActions map[string]map[string]slack.BlockAction) string {
var requestMap = make(map[string]string, 0) var requestMap = make(map[string]string, 0)
for _, actions := range blockActions { for _, actions := range blockActions {
for actionID, a := range actions { for actionID, a := range actions {
@@ -148,10 +113,5 @@ func buildUpdateIncidentStatusRequest(blockActions map[string]map[string]slack.B
} }
} }
selectedValue := requestMap["incident_status_modal_request"] return requestMap["incident_status_modal_request"]
selectedValueInInt, err := strconv.Atoi(selectedValue)
if err != nil {
logger.Error("String conversion to int faileed in buildUpdateIncidentResponderTeamRequest for "+selectedValue, zap.Error(err))
}
return uint(selectedValueInInt)
} }

View File

@@ -41,8 +41,8 @@ func (action *OpenFillRCAViewModalCommandAction) PerformAction(evt *socketmode.E
} }
tagsAction := NewIncidentTagsAction(appcontext.GetIncidentRepo(), appcontext.GetTagRepo()) tagsAction := NewIncidentTagsAction(appcontext.GetIncidentRepo(), appcontext.GetTagRepo())
rcaSummaryAction := NewIncidentRCASummaryAction(appcontext.GetIncidentRepo()) rcaSummaryAction := NewIncidentRCASummaryAction()
jiraLinksAction := NewIncidentJiraLinksAction(appcontext.GetIncidentService(), appcontext.GetSlackService()) jiraLinksAction := NewIncidentJiraLinksAction(appcontext.GetSlackService())
rcaSectionAction := NewIncidentRCASectionAction( rcaSectionAction := NewIncidentRCASectionAction(
action.socketModeClient, appcontext.GetIncidentRepo(), appcontext.GetTeamRepo(), action.socketModeClient, appcontext.GetIncidentRepo(), appcontext.GetTeamRepo(),
appcontext.GetTagRepo(), appcontext.GetSeverityRepo(), tagsAction, rcaSummaryAction, appcontext.GetTagRepo(), appcontext.GetSeverityRepo(), tagsAction, rcaSummaryAction,

View File

@@ -40,8 +40,8 @@ func (action *ResolveIncidentCommandAction) PerformAction(evt *socketmode.Event)
return return
} }
tagsAction := NewIncidentTagsAction(appcontext.GetIncidentRepo(), appcontext.GetTagRepo()) tagsAction := NewIncidentTagsAction(appcontext.GetIncidentRepo(), appcontext.GetTagRepo())
rcaSummaryAction := NewIncidentRCASummaryAction(appcontext.GetIncidentRepo()) rcaSummaryAction := NewIncidentRCASummaryAction()
jiraLinksAction := NewIncidentJiraLinksAction(appcontext.GetIncidentService(), appcontext.GetSlackService()) jiraLinksAction := NewIncidentJiraLinksAction(appcontext.GetSlackService())
rcaSectionAction := NewIncidentRCASectionAction( rcaSectionAction := NewIncidentRCASectionAction(
action.socketModeClient, appcontext.GetIncidentRepo(), appcontext.GetTeamRepo(), action.socketModeClient, appcontext.GetIncidentRepo(), appcontext.GetTeamRepo(),
appcontext.GetTagRepo(), appcontext.GetSeverityRepo(), tagsAction, rcaSummaryAction, appcontext.GetTagRepo(), appcontext.GetSeverityRepo(), tagsAction, rcaSummaryAction,

View File

@@ -4,11 +4,9 @@ import (
"fmt" "fmt"
"github.com/slack-go/slack" "github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"houston/appcontext" "houston/appcontext"
"houston/common/metrics" "houston/common/metrics"
incidentHelper "houston/common/util"
"houston/common/util/structUtil" "houston/common/util/structUtil"
"houston/internal" "houston/internal"
"houston/internal/processor/action/view" "houston/internal/processor/action/view"
@@ -18,7 +16,6 @@ import (
service "houston/service/request" service "houston/service/request"
"strconv" "strconv"
"strings" "strings"
"time"
) )
const setSeverityActionLogTag = "[set_severity_command_action]" const setSeverityActionLogTag = "[set_severity_command_action]"
@@ -80,116 +77,42 @@ func (action *SetSeverityCommandAction) setSeverity(cmd slack.SlashCommand, seve
return fmt.Errorf("%s is not a valid severity", severity) return fmt.Errorf("%s is not a valid severity", severity)
} }
if viper.GetBool("UPDATE_INCIDENT_V2_ENABLED") { incidentSeverityId := severityEntity.ID
incidentSeverityId := severityEntity.ID teamEntity, _, incidentStatusEntity, incidentChannels, err := appcontext.GetIncidentService().FetchAllEntitiesForIncident(incidentEntity)
teamEntity, _, incidentStatusEntity, incidentChannels, err := appcontext.GetIncidentService().FetchAllEntitiesForIncident(incidentEntity) if err != nil {
if err != nil { logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err))
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, err)) return err
return err }
}
if viper.GetBool("ENABLE_DE_ESCALATION_JUSTIFICATION") {
if incidentSeverityId > incidentEntity.SeverityId {
justificationMetadata := JustificationMetadata{SeverityId: incidentSeverityId, ChannelId: cmd.ChannelID}
stringData, stringErr := structUtil.StructToString(justificationMetadata)
if stringErr != nil {
logger.Error(fmt.Sprintf("error in converting justification metadata to string for incident id: %d", incidentEntity.ID), zap.Error(stringErr))
}
_, viewErr := action.socketModeClient.OpenView(cmd.TriggerID, view.CreateSeverityJustificationBlock(stringData))
if viewErr != nil {
logger.Error(fmt.Sprintf("error in opening justification view for incident with id: %d", incidentEntity.ID), zap.Error(viewErr))
return viewErr
}
return nil
}
}
if err := appcontext.GetIncidentService().UpdateSeverityId( if incidentSeverityId > incidentEntity.SeverityId {
service.UpdateIncidentRequest{ justificationMetadata := JustificationMetadata{SeverityId: incidentSeverityId, ChannelId: cmd.ChannelID}
Id: incidentEntity.ID, stringData, stringErr := structUtil.StructToString(justificationMetadata)
SeverityId: strconv.Itoa(int(incidentSeverityId)), if stringErr != nil {
}, logger.Error(fmt.Sprintf("error in converting justification metadata to string for incident id: %d", incidentEntity.ID), zap.Error(stringErr))
cmd.UserID, }
incidentEntity, _, viewErr := action.socketModeClient.OpenView(cmd.TriggerID, view.CreateSeverityJustificationBlock(stringData))
teamEntity, if viewErr != nil {
incidentStatusEntity, logger.Error(fmt.Sprintf("error in opening justification view for incident with id: %d", incidentEntity.ID), zap.Error(viewErr))
incidentChannels, return viewErr
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
return err
} }
return nil return nil
} }
incidentEntity.SeverityId = severityEntity.ID if err := appcontext.GetIncidentService().UpdateSeverityId(
incidentEntity.UpdatedBy = cmd.UserID service.UpdateIncidentRequest{
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla) Id: incidentEntity.ID,
err = appcontext.GetIncidentRepo().UpdateIncident(incidentEntity) SeverityId: strconv.Itoa(int(incidentSeverityId)),
if err != nil { },
logger.Error(fmt.Sprintf("%s failed to update severity for incident: %s. %+v", setSeverityActionLogTag, incidentEntity.IncidentName, err)) cmd.UserID,
return fmt.Errorf("failed to update severity for incident: %s", incidentEntity.IncidentName)
}
teamEntity, err := appcontext.GetTeamRepo().FindTeamById(incidentEntity.TeamId)
if err != nil {
logger.Error(fmt.Sprintf("%s error in finding team entity by team name %d. %+v", setSeverityActionLogTag, incidentEntity.TeamId, err))
return genericBackendError
}
if teamEntity == nil {
logger.Error(fmt.Sprintf("%s invalid team name %d. No entity found in DB", setSeverityActionLogTag, incidentEntity.TeamId))
return genericBackendError
}
for _, o := range severityEntity.SlackUserIds {
action.slackBot.InviteUsersToConversation(cmd.ChannelID, o)
}
go func() {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `set severity to %s (%s)`", cmd.UserID, severityEntity.Name, severityEntity.Description), false)
_, _, errMessage := action.socketModeClient.PostMessage(cmd.ChannelID, msgOption)
if errMessage != nil {
logger.Error("post response failed for IncidentUpdateSeverity", zap.Error(errMessage))
return
}
txt := fmt.Sprintf("set the channel topic: *%s · %s (%s) %s* | %s", teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.IncidentName, incidentEntity.Title)
att := slack.Attachment{
Text: txt,
Color: "#808080", // Grey color code
MarkdownIn: []string{"txt"}, // Define which fields support markdown
}
_, _, errMessage = action.socketModeClient.PostMessage(cmd.ChannelID, slack.MsgOptionAttachments(att))
if errMessage != nil {
logger.Error(fmt.Sprintf("%s post response failed for IncidentUpdateType. %+v", setSeverityActionLogTag, zap.Error(errMessage)))
return
}
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s",
teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.ID, incidentEntity.Title,
)
action.slackBot.SetChannelTopic(cmd.ChannelID, topic)
}()
incidentHelper.TagPseOrDevOncallToIncident(
cmd.ChannelID,
severityEntity,
teamEntity,
action.slackBot,
action.socketModeClient,
)
err = incidentHelper.AssignResponderToIncident(
appcontext.GetIncidentRepo(),
incidentEntity, incidentEntity,
teamEntity, teamEntity,
severityEntity, incidentStatusEntity,
action.socketModeClient, incidentChannels,
cmd.UserID, ); err != nil {
) logger.Error(fmt.Sprintf("error in updating severity: %v", err))
if err != nil { metrics.PublishHoustonFlowFailureMetrics(incidentService.UPDATE_INCIDENT_SEVERITY, err.Error())
logger.Error(fmt.Sprintf("%s Error while assigning responder to the incident %+v", setSeverityActionLogTag, zap.Error(err))) return err
return fmt.Errorf("severity is set to %s. Failed to assign responder post severity update", severity)
} }
go appcontext.GetIncidentService().SendAlert(incidentEntity)
return nil return nil
}) })
} }

View File

@@ -1,34 +1,22 @@
package action package action
import ( import (
"encoding/json" "github.com/slack-go/slack"
"fmt" "github.com/slack-go/slack/socketmode"
"go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
"houston/appcontext" "houston/appcontext"
"houston/common/metrics" "houston/common/metrics"
"houston/common/util"
stringUtil "houston/common/util/string" stringUtil "houston/common/util/string"
"houston/internal" "houston/internal"
"houston/internal/processor/action/view"
"houston/logger" "houston/logger"
"houston/model/incident" "houston/model/incident"
"houston/model/severity" "houston/model/severity"
"houston/model/team" "houston/model/team"
conference2 "houston/pkg/conference"
"houston/pkg/slackbot" "houston/pkg/slackbot"
"houston/service/conference"
incidentService "houston/service/incident"
incidentServiceImpl "houston/service/incident/impl" incidentServiceImpl "houston/service/incident/impl"
"houston/service/orchestration" "houston/service/orchestration"
request "houston/service/request/incident" request "houston/service/request/incident"
"strings"
"time"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap"
incidentHelper "houston/common/util"
) )
type CreateIncidentAction struct { type CreateIncidentAction struct {
@@ -51,157 +39,6 @@ func NewCreateIncidentProcessor(client *socketmode.Client, incidentService *inci
} }
} }
func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessing(callback slack.InteractionCallback, request *socketmode.Request) {
// Build create incident request
createIncidentRequest := buildCreateIncidentDTO(callback)
logger.Info("[CIP] incident request created", zap.Any("request", createIncidentRequest))
// Save the incident to the database
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
}
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
}
teamEntity, severityEntity, incidentStatusEntity, err := isp.getTeamAndSeverityAndStatus(incidentEntity.TeamId,
incidentEntity.SeverityId, incidentEntity.Status)
if err != nil {
logger.Error("[CIP] failed while getting team, severity and status", zap.Error(err))
return
}
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, severityEntity.Name, severityEntity.Description, incidentEntity.ID, incidentEntity.Title)
isp.slackbotClient.SetChannelTopic(channel.ID, topic)
go func() {
// Post incident summary to Blaze Group channel and incident channel
_, 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))
return
}
//Add user who created the incident
isp.slackbotClient.InviteUsersToConversation(incidentEntity.SlackChannel, incidentEntity.CreatedBy)
// add default users to the incident
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(channel.ID, severityEntity, teamEntity, isp.slackbotClient, isp.client)
}
err = incidentHelper.AssignResponderToIncident(
isp.incidentRepository,
incidentEntity,
teamEntity, severityEntity,
isp.client,
incidentEntity.CreatedBy,
)
if err != nil {
logger.Error("[CIP] error while assigning responder to the incident ", zap.Error(err))
}
err = incidentService.PostIncidentSLAMessageToChannel(incidentEntity, isp.severityRepository, isp.client)
if err != nil {
return
}
if viper.GetBool("ENABLE_CONFERENCE") {
calendarActions := conference2.GetCalendarActions()
calendarService := conference.NewCalendarService(calendarActions)
calendarEvent, err := calendarService.CreateEvent(incidentEntity.IncidentName)
if err != nil {
logger.Error(fmt.Sprintf("Unable to create conference event due to error : %s", err.Error()))
} else {
util.UpdateIncidentWithConferenceDetails(incidentEntity, calendarEvent, isp.incidentRepository)
msgUpdate := NewIncidentChannelMessageUpdateAction(isp.client, isp.incidentRepository, isp.teamRepository, isp.severityRepository)
blazeMessageUpdate := msgUpdate
msgUpdate.ProcessAction(incidentEntity.SlackChannel)
blazeMessageUpdate.ProcessAction(callback.Channel.ID)
bookmarkParam := slack.AddBookmarkParameters{Link: calendarEvent.ConferenceLink, Title: calendarService.GetConferenceTitle()}
_, 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(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()))
}
}
}
}()
// Acknowledge the interaction callback
var payload interface{}
isp.client.Ack(*request, payload)
go func() {
if len(teamEntity.WebhookSlackChannel) == 0 {
return
}
msg := fmt.Sprintf("*<@%s>* started an Incident\n `%s(%s) %s` :slack: <#%s>: %s",
incidentEntity.CreatedBy, severityEntity.Name, severityEntity.Description, teamEntity.Name, incidentEntity.SlackChannel, incidentEntity.Title)
msgOption := slack.MsgOptionText(msg, false)
_, _, errMessage := isp.client.PostMessage(teamEntity.WebhookSlackChannel, msgOption)
if errMessage != nil {
logger.Error("post response failed for ResolveIncident", zap.Error(errMessage))
return
}
}()
}
func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessingV2(
callback slack.InteractionCallback,
request *socketmode.Request,
) {
// Build create incident request
createIncidentRequest, err := buildCreateIncidentRequestV2(callback)
if err != nil {
logger.Error("[CIP] Error in building CreateIncidentRequestV2", zap.Error(err))
return
}
logger.Info("[CIP] incident request created", zap.Any("request", createIncidentRequest))
service := incidentServiceImpl.NewIncidentServiceV2(isp.db)
_, err = service.CreateIncident(*createIncidentRequest, "SLACK", callback.View.PrivateMetadata)
if err != nil {
logger.Error("[CIP] Error while creating incident", zap.Error(err))
metrics.PublishHoustonFlowFailureMetrics(incidentServiceImpl.CREATE_INCIDENT, err.Error())
return
}
// Acknowledge the interaction callback
var payload interface{}
isp.client.Ack(*request, payload)
}
func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessingV3( func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessingV3(
callback slack.InteractionCallback, callback slack.InteractionCallback,
request *socketmode.Request, request *socketmode.Request,
@@ -241,136 +78,6 @@ func (isp *CreateIncidentAction) CreateIncidentModalCommandProcessingV3(
isp.client.Ack(*request, payload) isp.client.Ack(*request, payload)
} }
func (isp *CreateIncidentAction) 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 {
isp.slackbotClient.InviteUsersToConversation(channelId, o)
}
return nil
}
func (isp *CreateIncidentAction) postIncidentSummary(blazeGroupChannelID, 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
var reportingTeamEntity *team.TeamEntity = nil
if incidentEntity.ReportingTeamId != nil {
var err error
reportingTeamEntity, err = isp.teamRepository.FindTeamById(*incidentEntity.ReportingTeamId)
if err != nil {
logger.Error(fmt.Sprintf("failed to get reporting team"), zap.Error(err))
}
}
blocks := view.IncidentSummarySectionV3(
incidentEntity, reportingTeamEntity, teamEntity, severityEntity, incidentStatusEntity,
)
color := util.GetColorBySeverity(incidentEntity.SeverityId)
att := slack.Attachment{Blocks: blocks, Color: color}
_, timestamp, err := isp.client.PostMessage(blazeGroupChannelID, slack.MsgOptionAttachments(att))
if err == nil {
err := isp.incidentRepository.CreateIncidentChannelEntry(&incident.CreateIncidentChannelEntry{
SlackChannel: blazeGroupChannelID,
MessageTimeStamp: timestamp,
IncidentId: incidentEntity.ID,
})
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("[CIP] error in saving message %v", err)
}
_, timestamp, err = isp.client.PostMessage(incidentChannelID, slack.MsgOptionAttachments(att))
if err == nil {
isp.incidentRepository.CreateIncidentChannelEntry(&incident.CreateIncidentChannelEntry{
SlackChannel: incidentChannelID,
MessageTimeStamp: timestamp,
IncidentId: incidentEntity.ID,
})
} else {
return nil, fmt.Errorf("[CIP] error in saving message %v", err)
}
return &timestamp, nil
}
func (isp *CreateIncidentAction) getTeamAndSeverityAndStatus(teamId, severityId, status uint) (*team.TeamEntity,
*severity.SeverityEntity, *incident.IncidentStatusEntity, error) {
teamEntity, err := isp.teamRepository.FindTeamById(teamId)
if err != nil || teamEntity == nil {
return nil, nil, nil, err
}
severityEntity, err := isp.severityRepository.FindSeverityById(severityId)
if err != nil || severityEntity == nil {
return nil, nil, nil, err
}
incidentStatusEntity, err := isp.incidentRepository.FindIncidentStatusById(status)
if err != nil || incidentStatusEntity == nil {
return nil, nil, nil, err
}
return teamEntity, severityEntity, incidentStatusEntity, nil
}
func buildCreateIncidentDTO(callback slack.InteractionCallback) *incident.CreateIncidentDTO {
blockActions := callback.View.State.Values
var createIncidentDTO incident.CreateIncidentDTO
var requestMap = make(map[string]string, 0)
for _, actions := range blockActions {
for actionID, a := range actions {
if string(a.Type) == string(slack.METPlainTextInput) {
requestMap[actionID] = a.Value
}
if string(a.Type) == slack.OptTypeStatic {
requestMap[actionID] = a.SelectedOption.Value
}
}
}
desRequestMap, _ := json.Marshal(requestMap)
json.Unmarshal(desRequestMap, &createIncidentDTO)
createIncidentDTO.Status = incident.Investigating
createIncidentDTO.StartTime = time.Now()
createIncidentDTO.EnableReminder = false
createIncidentDTO.CreatedBy = callback.User.ID
createIncidentDTO.UpdatedBy = callback.User.ID
return &createIncidentDTO
}
func buildCreateIncidentRequestV2(callback slack.InteractionCallback) (*request.CreateIncidentRequestV2, error) {
blockActions := callback.View.State.Values
var createIncidentRequest request.CreateIncidentRequestV2
var requestMap = make(map[string]string)
for _, actions := range blockActions {
for actionID, a := range actions {
if string(a.Type) == string(slack.METPlainTextInput) {
requestMap[actionID] = a.Value
}
if string(a.Type) == slack.OptTypeStatic {
requestMap[actionID] = a.SelectedOption.Value
}
}
}
desRequestMap, _ := json.Marshal(requestMap)
err := json.Unmarshal(desRequestMap, &createIncidentRequest)
if err != nil {
return nil, err
}
createIncidentRequest.CreatedBy = callback.User.ID
return &createIncidentRequest, nil
}
func buildCreateIncidentRequestV3(callback slack.InteractionCallback) (*request.CreateIncidentRequestV3, error) { func buildCreateIncidentRequestV3(callback slack.InteractionCallback) (*request.CreateIncidentRequestV3, error) {
blockActions := callback.View.State.Values blockActions := callback.View.State.Values
var createIncidentRequest request.CreateIncidentRequestV3 var createIncidentRequest request.CreateIncidentRequestV3

View File

@@ -39,7 +39,7 @@ type BlockActionProcessor struct {
incidentUpdateAction *action.UpdateIncidentAction incidentUpdateAction *action.UpdateIncidentAction
incidentUpdateTypeAction *action.IncidentUpdateTypeAction incidentUpdateTypeAction *action.IncidentUpdateTypeAction
incidentUpdateProductAction *action.IncidentUpdateProductAction incidentUpdateProductAction *action.IncidentUpdateProductAction
incidentUpdateSeverityAction *action.IncidentUpdateSevertityAction incidentUpdateSeverityAction *action.IncidentUpdateSeverityAction
incidentUpdateTitleAction *action.IncidentUpdateTitleAction incidentUpdateTitleAction *action.IncidentUpdateTitleAction
incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction
incidentDuplicateAction *action.DuplicateIncidentAction incidentDuplicateAction *action.DuplicateIncidentAction
@@ -76,7 +76,7 @@ func NewBlockActionProcessor(
incidentResolveAction: action.NewIncidentResolveProcessor(socketModeClient, incidentRepository, incidentResolveAction: action.NewIncidentResolveProcessor(socketModeClient, incidentRepository,
tagService, teamService, severityService, rcaService), tagService, teamService, severityService, rcaService),
incidentUpdateAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository, incidentUpdateAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository,
tagService, teamService, severityService), tagService, teamService, severityService, incidentServiceV2),
incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, incidentRepository, incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, incidentRepository,
teamService, severityService, slackbotClient, incidentServiceV2, orchestrator), teamService, severityService, slackbotClient, incidentServiceV2, orchestrator),
incidentUpdateProductAction: action.NewIncidentUpdateProductAction( incidentUpdateProductAction: action.NewIncidentUpdateProductAction(
@@ -99,8 +99,8 @@ func NewBlockActionProcessor(
incidentRCASectionAction: action.NewIncidentRCASectionAction( incidentRCASectionAction: action.NewIncidentRCASectionAction(
socketModeClient, incidentRepository, teamService, tagService, severityService, socketModeClient, incidentRepository, teamService, tagService, severityService,
action.NewIncidentTagsAction(incidentRepository, tagService), action.NewIncidentTagsAction(incidentRepository, tagService),
action.NewIncidentRCASummaryAction(incidentRepository), action.NewIncidentRCASummaryAction(),
action.NewIncidentJiraLinksAction(incidentServiceV2, slackService), action.NewIncidentJiraLinksAction(slackService),
rcaService, appcontext.GetIncidentService(), rcaService, appcontext.GetIncidentService(),
), ),
incidentGenerateRCAAction: action.NewIncidentGenerateRCAAction(socketModeClient), incidentGenerateRCAAction: action.NewIncidentGenerateRCAAction(socketModeClient),
@@ -229,7 +229,7 @@ type ViewSubmissionProcessor struct {
updateIncidentAction *action.UpdateIncidentAction updateIncidentAction *action.UpdateIncidentAction
incidentUpdateTitleAction *action.IncidentUpdateTitleAction incidentUpdateTitleAction *action.IncidentUpdateTitleAction
incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction
incidentUpdateSeverityAction *action.IncidentUpdateSevertityAction incidentUpdateSeverityAction *action.IncidentUpdateSeverityAction
incidentUpdateProductAction *action.IncidentUpdateProductAction incidentUpdateProductAction *action.IncidentUpdateProductAction
incidentUpdateTypeAction *action.IncidentUpdateTypeAction incidentUpdateTypeAction *action.IncidentUpdateTypeAction
incidentAdditionalAction *action.IncidentRCASectionAction incidentAdditionalAction *action.IncidentRCASectionAction
@@ -264,7 +264,7 @@ func NewViewSubmissionProcessor(
selectProductAction: action.NewStartIncidentBlockAction(socketModeClient, teamService, severityService, orchestrator), selectProductAction: action.NewStartIncidentBlockAction(socketModeClient, teamService, severityService, orchestrator),
assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, incidentRepository), assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, incidentRepository),
updateIncidentAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository, updateIncidentAction: action.NewIncidentUpdateAction(socketModeClient, incidentRepository,
tagService, teamService, severityService), tagService, teamService, severityService, incidentServiceV2),
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, incidentRepository, incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, incidentRepository,
teamService, severityService, slackbotClient), teamService, severityService, slackbotClient),
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, incidentRepository), incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, incidentRepository),
@@ -289,8 +289,8 @@ func NewViewSubmissionProcessor(
incidentRCAAction: action.NewIncidentRCASectionAction( incidentRCAAction: action.NewIncidentRCASectionAction(
socketModeClient, incidentRepository, teamService, tagService, severityService, socketModeClient, incidentRepository, teamService, tagService, severityService,
action.NewIncidentTagsAction(incidentRepository, tagService), action.NewIncidentTagsAction(incidentRepository, tagService),
action.NewIncidentRCASummaryAction(incidentRepository), action.NewIncidentRCASummaryAction(),
action.NewIncidentJiraLinksAction(incidentServiceV2, slackService), action.NewIncidentJiraLinksAction(slackService),
rcaService, appcontext.GetIncidentService(), rcaService, appcontext.GetIncidentService(),
), ),
} }

View File

@@ -1,7 +1,7 @@
package service package service
import ( import (
logger "houston/logger" "houston/logger"
"houston/model/incident" "houston/model/incident"
"houston/model/log" "houston/model/log"
"houston/model/severity" "houston/model/severity"

View File

@@ -152,7 +152,7 @@ func NewIncidentServiceV2(db *gorm.DB) *IncidentServiceV2 {
return incidentService return incidentService
} }
const logTag = "[create-incident-v2]" const logTag = "[create-incident]"
const updateLogTag = "[update-incident-v2]" const updateLogTag = "[update-incident-v2]"
const resolveLogTag = "[resolve-incident]" const resolveLogTag = "[resolve-incident]"
const updateSeveritySlackActionCount = 6 const updateSeveritySlackActionCount = 6
@@ -173,102 +173,6 @@ func (i *IncidentServiceV2) UpdateIncidentEntity(entity *incident.IncidentEntity
return i.incidentRepository.UpdateIncident(entity) return i.incidentRepository.UpdateIncident(entity)
} }
func (i *IncidentServiceV2) CreateIncident(
request incidentRequest.CreateIncidentRequestV2,
source string,
blazeGroupChannelID string,
) (service.IncidentResponse, error) {
emptyResponse := service.IncidentResponse{}
// Create incident dto
logger.Info(fmt.Sprintf("%s received request to create incident: %+v", logTag, request))
responderTeam, severityEntity, err := getTeamAndSeverityEntity(i, request.TeamID, request.SeverityID)
if err != nil {
return emptyResponse, err
}
incidentDTO, err := buildCreateIncidentDTO(request, i.teamRepository, i.severityRepository, i.slackService)
if err != nil {
return emptyResponse, err
}
logger.Info(fmt.Sprintf("%s CreateIncidentDTO created", logTag))
// 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(fmt.Sprintf("%s Error while creating incident", logTag), zap.Error(err))
tx.Rollback()
return emptyResponse, err
}
incidentName := incidentEntity.IncidentName
logger.Info(fmt.Sprintf("%s Incident entity created. Incident is: %s", logTag, incidentName))
// Create slack channel
// Call slack service to create a slack channel for incident
channel, err := i.slackService.CreateSlackChannel(incidentEntity.ID)
if err != nil {
logger.Error(
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
incidentEntity.UpdatedBy = incidentEntity.CreatedBy
err = i.incidentRepository.UpdateIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("%s [%s] Failed to update the slack channel details in DB", logTag, incidentName))
return emptyResponse, err
}
logger.Info(fmt.Sprintf("%s [%s] Slack channel details updated to incident entity", logTag, incidentName))
// Post incident summary
// Call slack service, provide required message to be posted
incidentStatusEntity, err := getIncidentStatusEntity(i, incidentEntity.ID, incidentEntity.Status, incidentName)
if err != nil {
return emptyResponse, err
}
logger.Info(fmt.Sprintf("%s [%s] Team, Severity and IncidentStatus entity fetched successfully", logTag, incidentName))
err = i.slackService.SetChannelTopic(channel, responderTeam, severityEntity, incidentEntity)
if err != nil {
logger.Error(
fmt.Sprintf("%s [%s] Failed to set channel topic", logTag, incidentName),
zap.Error(err),
)
}
logger.Info(fmt.Sprintf("%s [%s] Channel topic is set", logTag, incidentName))
go func() {
err := createIncidentWorkflow(
i,
channel,
incidentEntity,
responderTeam,
severityEntity,
incidentStatusEntity,
blazeGroupChannelID,
)
if err != nil {
metrics.PublishHoustonFlowFailureMetrics(CREATE_INCIDENT, err.Error())
return
}
i.HandleKrakatoaWorkflow(incidentEntity)
}()
go postInWebhookSlackChannel(i, responderTeam, incidentEntity, severityEntity)
go i.SendAlert(incidentEntity)
return service.ConvertToIncidentResponse(*incidentEntity), nil
}
func (i *IncidentServiceV2) PostIncidentCreationWorkflow( func (i *IncidentServiceV2) PostIncidentCreationWorkflow(
channel *slackClient.Channel, channel *slackClient.Channel,
incidentID uint, incidentID uint,
@@ -1028,52 +932,6 @@ func assignResponderToIncident(
return nil return nil
} }
/*
takes create incident request, team, severity repo and slack service as input
builds data transfer object with required properties which will be used
by the repository function to create and commit incident entity
*/
func buildCreateIncidentDTO(
createIncRequest incidentRequest.CreateIncidentRequestV2,
teamRepository team.ITeamRepository,
severityRepository severity.ISeverityRepository,
slackService slack.ISlackService,
) (*incident.CreateIncidentDTO, error) {
var createIncidentRequest incident.CreateIncidentDTO
teamID, err := strconv.ParseUint(createIncRequest.TeamID, 10, 64)
if err != nil {
//todo handle error
}
teamEntity, err := teamRepository.FindTeamById(uint(teamID))
if err != nil {
return nil, err
}
severityID, err := strconv.ParseUint(createIncRequest.SeverityID, 10, 64)
if err != nil {
//todo handle error
}
severityEntity, err := severityRepository.FindSeverityById(uint(severityID))
if err != nil {
return nil, err
}
var rawJson, _ = json.Marshal([]incidentRequest.CreateIncidentMetadata{createIncRequest.Metadata})
createdBy := slackService.GetSlackUserIdOrEmail(createIncRequest.CreatedBy)
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
}
/* /*
returns incident status entity for a given status ID returns incident status entity for a given status ID
*/ */
@@ -1507,28 +1365,17 @@ func (i *IncidentServiceV2) UpdateSeverityId(
} }
if incidentEntity.SeverityId != uint(num) { if incidentEntity.SeverityId != uint(num) {
if viper.GetBool("ENABLE_DE_ESCALATION_JUSTIFICATION") { if uint(num) > incidentEntity.SeverityId && request.Justification == "" {
if uint(num) > incidentEntity.SeverityId && request.Justification == "" { return fmt.Errorf("justification is required for de-escalating the incident")
return fmt.Errorf("justification is required for de-escalating the incident") }
} incidentEntity.SeverityId = uint(num)
incidentEntity.SeverityId = uint(num) incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla) incidentEntity.UpdatedAt = time.Now()
incidentEntity.UpdatedAt = time.Now() incidentEntity.UpdatedBy = userId
incidentEntity.UpdatedBy = userId err := i.incidentRepository.UpdateIncidentWithJustification(incidentEntity, request.Justification)
err := i.incidentRepository.UpdateIncidentWithJustification(incidentEntity, request.Justification) if err != nil {
if err != nil { logger.Error(fmt.Sprintf("%s error in committing update to DB", updateLogTag), zap.Error(err))
logger.Error(fmt.Sprintf("%s error in committing update to DB", updateLogTag), zap.Error(err)) return err
return err
}
} else {
incidentEntity.SeverityId = uint(num)
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
err := i.commitIncidentEntity(incidentEntity, userId)
if err != nil {
logger.Error(fmt.Sprintf("%s error in committing update to DB", updateLogTag), zap.Error(err))
return err
}
} }
err = i.UpdateSeverityWorkflow( err = i.UpdateSeverityWorkflow(

View File

@@ -5,14 +5,11 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"houston/model/incident" "houston/model/incident"
request "houston/service/request" request "houston/service/request"
incidentRequest "houston/service/request/incident"
service "houston/service/response"
) )
type IIncidentService interface { type IIncidentService interface {
CreateIncidentEntity(incidentDTO *incident.CreateIncidentDTO, tx *gorm.DB) (*incident.IncidentEntity, error) CreateIncidentEntity(incidentDTO *incident.CreateIncidentDTO, tx *gorm.DB) (*incident.IncidentEntity, error)
UpdateIncidentEntity(entity *incident.IncidentEntity) error UpdateIncidentEntity(entity *incident.IncidentEntity) error
CreateIncident(request incidentRequest.CreateIncidentRequestV2, source string, blazeGroupChannelID string) (service.IncidentResponse, error)
PostIncidentCreationWorkflow( PostIncidentCreationWorkflow(
channel *slackClient.Channel, channel *slackClient.Channel,
incidentID uint, incidentID uint,

View File

@@ -1,15 +1,10 @@
package service package service
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"houston/appcontext" "houston/appcontext"
"houston/common/util"
slackUtil "houston/common/util/slack"
"houston/internal/processor/action" "houston/internal/processor/action"
"houston/internal/processor/action/view" "houston/logger"
logger "houston/logger"
"houston/model/incident" "houston/model/incident"
"houston/model/log" "houston/model/log"
"houston/model/product" "houston/model/product"
@@ -17,11 +12,9 @@ import (
"houston/model/team" "houston/model/team"
"houston/model/user" "houston/model/user"
"houston/pkg/slackbot" "houston/pkg/slackbot"
incidentV2 "houston/service/incident"
"houston/service/incident/impl" "houston/service/incident/impl"
rcaService "houston/service/rca/impl" rcaService "houston/service/rca/impl"
request "houston/service/request" request "houston/service/request"
incidentRequest "houston/service/request/incident"
service "houston/service/response" service "houston/service/response"
common "houston/service/response/common" common "houston/service/response/common"
slack2 "houston/service/slack" slack2 "houston/service/slack"
@@ -29,7 +22,6 @@ import (
utils "houston/service/utils" utils "houston/service/utils"
"math" "math"
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -374,109 +366,6 @@ func (i *incidentService) GetTeamIncidents(c *gin.Context) {
c.JSON(http.StatusOK, common.SuccessResponse(incidents, http.StatusOK)) 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) { func (i *incidentService) InvitePseOnCallPersonToIncident(channelId, ts string) {
go func() { go func() {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
@@ -493,354 +382,6 @@ func (i *incidentService) InvitePseOnCallPersonToIncident(channelId, ts string)
}() }()
} }
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
var reportingTeamEntity *team.TeamEntity = nil
if incidentEntity.ReportingTeamId != nil {
var err error
reportingTeamEntity, err = i.teamRepository.FindTeamById(*incidentEntity.ReportingTeamId)
if err != nil {
logger.Error(fmt.Sprintf("failed to get reporting team"), zap.Error(err))
}
}
blocks := view.IncidentSummarySectionV3(
incidentEntity, reportingTeamEntity, 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 &timestamp, 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 !reflect.DeepEqual(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
}
func teamDTOToIncidentHeaderOption(t *team.TeamDTO) *service.IncidentHeaderOption { func teamDTOToIncidentHeaderOption(t *team.TeamDTO) *service.IncidentHeaderOption {
return &service.IncidentHeaderOption{ return &service.IncidentHeaderOption{
Value: t.ID, Value: t.ID,

View File

@@ -6,7 +6,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
metrics "houston/common/metrics" "houston/common/metrics"
"houston/common/util" "houston/common/util"
"houston/common/util/channel" "houston/common/util/channel"
"houston/common/util/file" "houston/common/util/file"

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
logger "houston/logger" "houston/logger"
"houston/model/log" "houston/model/log"
service "houston/service/response" service "houston/service/response"
common "houston/service/response/common" common "houston/service/response/common"

View File

@@ -289,7 +289,7 @@ func (r *RcaService) uploadSlackConversationHistory(channelId string, incidentId
r.extractUserIDs(&conversations, userIDtoNameMap) r.extractUserIDs(&conversations, userIDtoNameMap)
if len(userIDtoNameMap) > 0 { if len(userIDtoNameMap) > 0 {
var slackUserIds []string var slackUserIds []string
for slackUserId, _ := range userIDtoNameMap { for slackUserId := range userIDtoNameMap {
slackUserIds = append(slackUserIds, slackUserId) slackUserIds = append(slackUserIds, slackUserId)
} }

View File

@@ -12,7 +12,7 @@ import (
"houston/logger" "houston/logger"
"houston/mocks" "houston/mocks"
"houston/model" "houston/model"
incident "houston/model/incident" "houston/model/incident"
"houston/model/rca" "houston/model/rca"
"houston/model/rcaInput" "houston/model/rcaInput"
"houston/model/user" "houston/model/user"
@@ -248,7 +248,7 @@ func TestExtractUserIds(t *testing.T) {
rcaService.extractUserIDs(testConversation, testUserIdMap) rcaService.extractUserIDs(testConversation, testUserIdMap)
assert.Equal(t, len(expectedUserIdMap), len(testUserIdMap)) assert.Equal(t, len(expectedUserIdMap), len(testUserIdMap))
for key, _ := range expectedUserIdMap { for key := range expectedUserIdMap {
assert.Contains(t, testUserIdMap, key) assert.Contains(t, testUserIdMap, key)
} }
// if there is no user id in the conversation text // if there is no user id in the conversation text

View File

@@ -5,24 +5,6 @@ import (
"time" "time"
) )
type CreateIncidentRequest struct {
Title string `gorm:"column:title"`
Description string `gorm:"column:description"`
SeverityName string `gorm:"column:severityName"`
TeamName string `gorm:"column:teamName"`
CreatedBy string `gorm:"column:createdBy"`
MetaData CreateIncidentMetadata `gorm:"json:metaData"`
}
type CreateIncidentRequestV2 struct {
Title string `json:"title"`
Description string `json:"description"`
SeverityID string `json:"severity"`
TeamID string `json:"type"`
CreatedBy string `json:"createdBy"`
Metadata CreateIncidentMetadata `json:"metaData"`
}
type CreateIncidentRequestV3 struct { type CreateIncidentRequestV3 struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`

View File

@@ -7,7 +7,7 @@ import (
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
metrics "houston/common/metrics" "houston/common/metrics"
"houston/common/util" "houston/common/util"
"houston/logger" "houston/logger"
"houston/model/incident" "houston/model/incident"

View File

@@ -147,17 +147,17 @@ func (suite *TagServiceSuite) Test_GetMandatoryActiveTags_SuccessCase() {
func getMockTagDTO() *[]tag.TagDTO { func getMockTagDTO() *[]tag.TagDTO {
return &[]tag.TagDTO{ return &[]tag.TagDTO{
tag.TagDTO{ {
Id: 1, Id: 1,
Label: "Business affected", Label: "Business affected",
PlaceHolder: "select business affected", PlaceHolder: "select business affected",
}, },
tag.TagDTO{ {
Id: 2, Id: 2,
Label: "Contributing factors", Label: "Contributing factors",
PlaceHolder: "select contributing factors", PlaceHolder: "select contributing factors",
}, },
tag.TagDTO{ {
Id: 3, Id: 3,
Label: "Additional tags", Label: "Additional tags",
PlaceHolder: "select additional tags", PlaceHolder: "select additional tags",
@@ -167,11 +167,11 @@ func getMockTagDTO() *[]tag.TagDTO {
func getMockTagValues() *[]tag.TagValueEntity { func getMockTagValues() *[]tag.TagValueEntity {
return &[]tag.TagValueEntity{ return &[]tag.TagValueEntity{
tag.TagValueEntity{ {
TagId: 1, TagId: 1,
Value: "Mock Value 1", Value: "Mock Value 1",
}, },
tag.TagValueEntity{ {
TagId: 2, TagId: 2,
Value: "Mock Value 2", Value: "Mock Value 2",
}, },

View File

@@ -9,7 +9,7 @@ import (
"houston/common/util" "houston/common/util"
"houston/logger" "houston/logger"
"houston/model/customErrors" "houston/model/customErrors"
externalTeam "houston/model/externalTeam" "houston/model/externalTeam"
"houston/model/incident" "houston/model/incident"
"houston/model/team" "houston/model/team"
teamSeverityModel "houston/model/teamSeverity" teamSeverityModel "houston/model/teamSeverity"

View File

@@ -7,8 +7,8 @@ import (
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
util "houston/common/util" "houston/common/util"
logger "houston/logger" "houston/logger"
"houston/model/incident" "houston/model/incident"
"houston/model/log" "houston/model/log"
"houston/model/severity" "houston/model/severity"

View File

@@ -75,42 +75,6 @@ func ValidateResolveIncidentRequest(request service.ResolveIncidentRequest) erro
return nil return nil
} }
func ValidateCreateIncidentRequest(request incident.CreateIncidentRequest) error {
if request.Title == "" || request.Description == "" {
return errors.New("title and description should be present in create request")
}
descriptionMaxLength := viper.GetInt("create-incident.description.max-length")
titleMaxLength := viper.GetInt("create-incident.title.max-length")
if len(request.Description) > descriptionMaxLength {
return errors.New(fmt.Sprintf("description should not be more than %v characters long", descriptionMaxLength))
}
if len(request.Title) > titleMaxLength {
return errors.New(fmt.Sprintf("title should not be more than %v characters long", titleMaxLength))
}
if request.CreatedBy == "" {
return errors.New("created By should be present")
}
return nil
}
func ValidateCreateIncidentRequestV2(request incident.CreateIncidentRequestV2) error {
if request.Title == "" || request.Description == "" {
return errors.New("title and description should be present in create request")
}
descriptionMaxLength := viper.GetInt("create-incident.description.max-length")
titleMaxLength := viper.GetInt("create-incident.title.max-length")
if len(request.Description) > descriptionMaxLength {
return errors.New(fmt.Sprintf("description should not be more than %v characters long", descriptionMaxLength))
}
if len(request.Title) > titleMaxLength {
return errors.New(fmt.Sprintf("title should not be more than %v characters long", titleMaxLength))
}
if request.CreatedBy == "" {
return errors.New("created By should be present")
}
return nil
}
func ValidateCreateIncidentRequestV3(request incident.CreateIncidentRequestV3) error { func ValidateCreateIncidentRequestV3(request incident.CreateIncidentRequestV3) error {
if request.Title == "" || request.Description == "" { if request.Title == "" || request.Description == "" {
return errors.New("title and description should be present in create request") return errors.New("title and description should be present in create request")