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:
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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{
|
||||||
|
|||||||
@@ -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{}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ×tamp, 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
|
||||||
|
|||||||
@@ -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(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 ×tamp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) getTeamAndSeverityAndStatus(teamId, severityId, status uint) (*team.TeamEntity,
|
|
||||||
*severity.SeverityEntity, *incident.IncidentStatusEntity, error) {
|
|
||||||
teamEntity, err := i.teamRepository.FindTeamById(teamId)
|
|
||||||
if err != nil || teamEntity == nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
severityEntity, err := i.severityRepository.FindSeverityById(severityId)
|
|
||||||
if err != nil || severityEntity == nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
incidentStatusEntity, err := i.incidentRepository.FindIncidentStatusById(status)
|
|
||||||
if err != nil || incidentStatusEntity == nil {
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return teamEntity, severityEntity, incidentStatusEntity, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) buildCreateIncidentDTO(createIncRequest incidentRequest.CreateIncidentRequest) (*incident.CreateIncidentDTO, error) {
|
|
||||||
var createIncidentRequest incident.CreateIncidentDTO
|
|
||||||
teamEntity, err := i.teamRepository.FindTeamByTeamName(createIncRequest.TeamName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
severityEntity, err := i.severityRepository.FindSeverityByName(createIncRequest.SeverityName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawJson, _ = json.Marshal([]incidentRequest.CreateIncidentMetadata{createIncRequest.MetaData})
|
|
||||||
createdBy := slackUtil.GetSlackUserIdFromEmail(createIncRequest.CreatedBy, i.slackbotClient)
|
|
||||||
|
|
||||||
createIncidentRequest.Title = createIncRequest.Title
|
|
||||||
createIncidentRequest.Description = createIncRequest.Description
|
|
||||||
createIncidentRequest.Severity = fmt.Sprintf("%d", severityEntity.ID)
|
|
||||||
createIncidentRequest.TeamId = fmt.Sprintf("%d", teamEntity.ID)
|
|
||||||
createIncidentRequest.Status = incident.Investigating
|
|
||||||
createIncidentRequest.CreatedBy = createdBy
|
|
||||||
createIncidentRequest.UpdatedBy = createdBy
|
|
||||||
createIncidentRequest.StartTime = time.Now()
|
|
||||||
createIncidentRequest.EnableReminder = false
|
|
||||||
createIncidentRequest.MetaData = rawJson
|
|
||||||
return &createIncidentRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) UpdateIncident(c *gin.Context) {
|
|
||||||
userEmail := c.GetHeader("X-User-Email")
|
|
||||||
|
|
||||||
var updateIncidentRequest request.UpdateIncidentRequest
|
|
||||||
if err := c.ShouldBindJSON(&updateIncidentRequest); err != nil {
|
|
||||||
c.JSON(http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := utils.ValidateUpdateIncidentRequest(updateIncidentRequest, userEmail)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
incidentEntity, err := i.incidentRepository.FindIncidentById(updateIncidentRequest.Id)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in fetching incident", zap.Any("incidentId", updateIncidentRequest.Id), zap.Error(err))
|
|
||||||
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if incidentEntity == nil {
|
|
||||||
logger.Info("incident not found", zap.Any("incidentId", updateIncidentRequest.Id))
|
|
||||||
c.JSON(http.StatusBadRequest, common.ErrorResponse(errors.New(fmt.Sprintf("incident with id: %v not found", updateIncidentRequest.Id)), http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfo, err := i.slackbotClient.GetUserByEmail(userEmail)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(fmt.Sprintf("error in fetching user by email: %v, hence using user email as user Id", userEmail), zap.String("userEmail", userEmail), zap.Error(err))
|
|
||||||
userInfo = &slack.User{
|
|
||||||
ID: userEmail,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = i.UpdateSeverityId(updateIncidentRequest, userInfo.ID, incidentEntity)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in updating severity", zap.Error(err))
|
|
||||||
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = i.UpdateStatus(updateIncidentRequest, userInfo.ID, incidentEntity.SlackChannel, incidentEntity)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in updating status", zap.Error(err))
|
|
||||||
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = i.UpdateTeamId(updateIncidentRequest, userInfo.ID, incidentEntity)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in updating teamId", zap.Error(err))
|
|
||||||
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = i.UpdateMetaData(updateIncidentRequest, incidentEntity, userInfo.ID)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in updating metadata", zap.Error(err))
|
|
||||||
c.JSON(http.StatusInternalServerError, common.ErrorResponse(err, http.StatusBadRequest, nil))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
incidentEntity.UpdatedAt = time.Now()
|
|
||||||
incidentEntity.UpdatedBy = userInfo.ID
|
|
||||||
i.incidentRepository.UpdateIncident(incidentEntity)
|
|
||||||
|
|
||||||
i.messageUpdateAction.ProcessAction(incidentEntity.SlackChannel)
|
|
||||||
|
|
||||||
if updateIncidentRequest.SeverityId != "" || updateIncidentRequest.TeamId != "" {
|
|
||||||
teamEntity, err := i.teamRepository.FindTeamById(incidentEntity.TeamId)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(fmt.Sprintf("error in fetching team by id: %v", incidentEntity.TeamId), zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
incidentSeverityEntity, err := i.severityRepository.FindIncidentSeverityEntityById(int(incidentEntity.SeverityId))
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(fmt.Sprintf("error in fetching severity by id: %v", incidentEntity.SeverityId), zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
topic := fmt.Sprintf("%s-%s(%s) Incident-%d | %s", teamEntity.Name, incidentSeverityEntity.Name, incidentSeverityEntity.Description, incidentEntity.ID, incidentEntity.Title)
|
|
||||||
i.socketModeClient.SetTopicOfConversation(incidentEntity.SlackChannel, topic)
|
|
||||||
if !util.IsBlank(updateIncidentRequest.TeamId) {
|
|
||||||
go i.incidentServiceV2.HandleKrakatoaWorkflow(incidentEntity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, common.SuccessResponse(incidentEntity.SlackChannel, http.StatusOK))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) UpdateSeverityId(
|
|
||||||
updateIncidentRequest request.UpdateIncidentRequest, userId string, incidentEntity *incident.IncidentEntity) error {
|
|
||||||
if updateIncidentRequest.SeverityId != "" {
|
|
||||||
num, err := strconv.ParseUint(updateIncidentRequest.SeverityId, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in string to int conversion",
|
|
||||||
zap.String("SeverityId", updateIncidentRequest.SeverityId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
severityEntity, err := i.severityRepository.FindSeverityById(uint(num))
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in fetching severity", zap.Any("severity", incidentEntity.SeverityId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if updateIncidentRequest.SeverityId != "" {
|
|
||||||
num, err := strconv.ParseUint(updateIncidentRequest.SeverityId, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in string to int conversion",
|
|
||||||
zap.String("SeverityId", updateIncidentRequest.SeverityId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if incidentEntity.SeverityId != uint(num) {
|
|
||||||
incidentEntity.SeverityId = uint(num)
|
|
||||||
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)
|
|
||||||
errMessage := util.PostIncidentSeverityUpdateMessage(userId, severityEntity.Name, incidentEntity.SlackChannel, i.socketModeClient)
|
|
||||||
if errMessage != nil {
|
|
||||||
logger.Error("post response failed for IncidentUpdateSeverity", zap.Error(errMessage))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) UpdateStatus(
|
|
||||||
updateIncidentRequest request.UpdateIncidentRequest, userId, slackChannel string,
|
|
||||||
incidentEntity *incident.IncidentEntity) error {
|
|
||||||
if updateIncidentRequest.Status != "" {
|
|
||||||
num, err := strconv.ParseUint(updateIncidentRequest.Status, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in string to int conversion",
|
|
||||||
zap.String("Status", updateIncidentRequest.Status), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if incidentEntity.Status != uint(num) {
|
|
||||||
incidentEntity.Status = uint(num)
|
|
||||||
incidentStatus, _ := i.incidentRepository.FindIncidentStatusById(incidentEntity.Status)
|
|
||||||
errMessage := util.PostIncidentStatusUpdateMessage(userId, incidentStatus.Name, slackChannel, i.socketModeClient)
|
|
||||||
if errMessage != nil {
|
|
||||||
logger.Error("post response failed for IncidentUpdateStatus", zap.Error(errMessage))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if incidentStatus.IsTerminalStatus {
|
|
||||||
now := time.Now()
|
|
||||||
incidentEntity.EndTime = &now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) UpdateTeamId(
|
|
||||||
updateIncidentRequest request.UpdateIncidentRequest, userId string, incidentEntity *incident.IncidentEntity) error {
|
|
||||||
if updateIncidentRequest.TeamId != "" {
|
|
||||||
severityEntity, err := i.severityRepository.FindSeverityById(incidentEntity.SeverityId)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in fetching severity", zap.Any("severity", incidentEntity.SeverityId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if updateIncidentRequest.TeamId != "" {
|
|
||||||
num, err := strconv.ParseUint(updateIncidentRequest.TeamId, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in string to int conversion",
|
|
||||||
zap.String("TeamId", updateIncidentRequest.TeamId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if incidentEntity.TeamId != uint(num) {
|
|
||||||
incidentEntity.TeamId = uint(num)
|
|
||||||
teamEntity, err := i.teamRepository.FindTeamById(incidentEntity.TeamId)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("error in fetching team by id",
|
|
||||||
zap.String("TeamId", updateIncidentRequest.TeamId), zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
errMessage := util.PostIncidentTypeUpdateMessage(
|
|
||||||
userId, teamEntity.Name, severityEntity.Name, severityEntity.Description,
|
|
||||||
incidentEntity.IncidentName, incidentEntity.Title, incidentEntity.SlackChannel, i.socketModeClient)
|
|
||||||
if errMessage != nil {
|
|
||||||
logger.Error("post response failed for IncidentUpdateType", zap.Error(errMessage))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = i.addDefaultUsersToIncident(incidentEntity.SlackChannel, teamEntity, severityEntity)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("[Update Incident] error while adding default users to incident", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
util.TagPseOrDevOncallToIncident(
|
|
||||||
incidentEntity.SlackChannel,
|
|
||||||
severityEntity,
|
|
||||||
teamEntity,
|
|
||||||
i.slackbotClient,
|
|
||||||
i.socketModeClient,
|
|
||||||
)
|
|
||||||
|
|
||||||
err = util.AssignResponderToIncident(
|
|
||||||
i.incidentRepository,
|
|
||||||
incidentEntity,
|
|
||||||
teamEntity,
|
|
||||||
severityEntity,
|
|
||||||
i.socketModeClient,
|
|
||||||
incidentEntity.UpdatedBy,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("[Update Incident][update team id] error while assigning responder to the incident ", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incidentService) UpdateMetaData(updateIncidentRequest request.UpdateIncidentRequest, incidentEntity *incident.IncidentEntity, userId string) error {
|
|
||||||
metadata := incidentRequest.CreateIncidentMetadata{}
|
|
||||||
if !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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user