TP-54496 | Created justification message prompt for de-escalation (#360)

* TP-54496| created justification message prompt for de-escalation

* TP-54496| added migration file to update new column in log table

* TP-54496| added feature flag

* TP-54496| created util functions and constants

* TP-54496| updated design changes

* TP-54496| made the requested changed in PR comments

* TP-54496| fixed bugs in merge conflicts

* TP-54496| acknowledging to slack before hand so to not time out

* TP-54496| modified log entity field justification

---------

Co-authored-by: Shashank Shekhar <shashank.shekhar@navi.com>
This commit is contained in:
Gullipalli Chetan Kumar
2024-02-07 18:11:50 +05:30
committed by GitHub
parent 44756e482d
commit a4c648649b
14 changed files with 245 additions and 31 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/spf13/viper"
incidentHelper "houston/common/util"
"houston/common/util/structUtil"
"houston/internal/processor/action/view"
"houston/logger"
"houston/model/incident"
@@ -48,6 +49,11 @@ func NewIncidentUpdateSeverityAction(
}
}
type JustificationMetadata struct {
SeverityId uint
ChannelId string
}
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityRequestProcess(callback slack.InteractionCallback, request *socketmode.Request) {
incidentSeverity, err := isp.severityRepository.GetAllActiveSeverity()
if err != nil || incidentSeverity == nil {
@@ -68,9 +74,53 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityRequestProcess(c
isp.client.Ack(*request, payload)
}
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverityJustification(callback slack.InteractionCallback, request *socketmode.Request, user slack.User) {
var justificationMetadata JustificationMetadata
err := structUtil.StringToStruct(callback.View.PrivateMetadata, &justificationMetadata)
if err != nil {
logger.Error(fmt.Sprintf("error in converting string to justification metadata for trigger id: %s, privatemetadata: %s",
callback.TriggerID, callback.View.PrivateMetadata), zap.Error(err))
return
}
severityId := justificationMetadata.SeverityId
channelId := justificationMetadata.ChannelId
incidentEntity, err := isp.incidentRepository.FindIncidentByChannelId(channelId)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching incident with channel id: %s", channelId), zap.Error(err))
return
} else if incidentEntity == nil {
logger.Error(fmt.Sprintf("incident not found with channel id: %s", channelId))
return
}
teamEntity, _, incidentStatusEntity, incidentChannels, err := isp.incidentServiceV2.FetchAllEntitiesForIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d", incidentEntity.ID), zap.Error(err))
return
}
var payload interface{}
isp.client.Ack(*request, payload)
if err := isp.incidentServiceV2.UpdateSeverityId(
service.UpdateIncidentRequest{
Id: incidentEntity.ID,
SeverityId: strconv.Itoa(int(severityId)),
Justification: callback.View.State.Values[view.JustificationBlockId][view.JustificationActionId].Value,
},
user.ID,
incidentEntity,
teamEntity,
incidentStatusEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
}
return
}
func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverity(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) {
incidentEntity, err := isp.incidentRepository.FindIncidentByChannelId(callback.View.PrivateMetadata)
channelId := callback.View.PrivateMetadata
incidentEntity, err := isp.incidentRepository.FindIncidentByChannelId(channelId)
if err != nil {
logger.Error("FindIncidentByChannelId error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
@@ -86,13 +136,28 @@ func (isp *IncidentUpdateSevertityAction) IncidentUpdateSeverity(callback slack.
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
}
var payload interface{}
isp.client.Ack(*request, payload)
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,

View File

@@ -4,12 +4,17 @@ import (
"fmt"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"github.com/spf13/viper"
"go.uber.org/zap"
"houston/appcontext"
incidentHelper "houston/common/util"
"houston/common/util/structUtil"
"houston/internal"
"houston/internal/processor/action/view"
"houston/logger"
"houston/pkg/slackbot"
service "houston/service/request"
"strconv"
"strings"
"time"
)
@@ -72,6 +77,47 @@ func (action *SetSeverityCommandAction) setSeverity(cmd slack.SlashCommand, seve
logger.Error(fmt.Sprintf("%s no DB entity found for %s. %+v", setSeverityActionLogTag, severity, err))
return fmt.Errorf("%s is not a valid severity", severity)
}
if viper.GetBool("UPDATE_INCIDENT_V2_ENABLED") {
incidentSeverityId := severityEntity.ID
teamEntity, _, incidentStatusEntity, incidentChannels, err := appcontext.GetIncidentService().FetchAllEntitiesForIncident(incidentEntity)
if err != nil {
logger.Error(fmt.Sprintf("error in fetching entities for incident with id: %d %v", incidentEntity.ID, 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(
service.UpdateIncidentRequest{
Id: incidentEntity.ID,
SeverityId: strconv.Itoa(int(incidentSeverityId)),
},
cmd.UserID,
incidentEntity,
teamEntity,
incidentStatusEntity,
incidentChannels,
); err != nil {
logger.Error(fmt.Sprintf("error in updating severity: %v", err))
return err
}
return nil
}
incidentEntity.SeverityId = severityEntity.ID
incidentEntity.UpdatedBy = cmd.UserID
incidentEntity.SeverityTat = time.Now().AddDate(0, 0, severityEntity.Sla)

View File

@@ -9,6 +9,11 @@ import (
"github.com/slack-go/slack"
)
const (
JustificationBlockId = "justificationBlockId"
JustificationActionId = "justificationActionId"
)
func BuildIncidentUpdateSeverityModal(channelID string, incidentSeverity []severity.SeverityEntity) slack.ModalViewRequest {
titleText := slack.NewTextBlockObject(slack.PlainTextType, "Set severity of incident", false, false)
closeText := slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false)
@@ -51,3 +56,31 @@ func createIncidentSeverityBlock(options []severity.SeverityEntity) []*slack.Opt
}
return optionBlockObjects
}
func CreateSeverityJustificationBlock(metadata string) slack.ModalViewRequest {
subTitle := slack.NewTextBlockObject(slack.PlainTextType, "Justification", false, false)
closeText := slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false)
submitText := slack.NewTextBlockObject(slack.PlainTextType, "Submit", false, false)
inputPlaceHolder := slack.NewTextBlockObject(slack.PlainTextType, "Provide reason for reducing the severity of this incident", false, false)
inputElement := slack.NewPlainTextInputBlockElement(inputPlaceHolder, JustificationActionId)
inputElement.MaxLength = 100
justificationBlock := slack.NewInputBlock(JustificationBlockId, subTitle, nil, inputElement)
justificationBlock.Optional = false
blocks := slack.Blocks{
BlockSet: []slack.Block{
justificationBlock,
},
}
return slack.ModalViewRequest{
Type: slack.VTModal,
Title: subTitle,
Close: closeText,
Submit: submitText,
Blocks: blocks,
PrivateMetadata: metadata,
CallbackID: util.SeverityJustificationSubmit,
}
}

View File

@@ -295,6 +295,10 @@ func (vsp *ViewSubmissionProcessor) ProcessCommand(callback slack.InteractionCal
{
vsp.incidentUpdateSeverityAction.IncidentUpdateSeverity(callback, request, callback.Channel, callback.User)
}
case util.SeverityJustificationSubmit:
{
vsp.incidentUpdateSeverityAction.IncidentUpdateSeverityJustification(callback, request, callback.User)
}
case util.SetIncidentTypeSubmit:
{
vsp.incidentUpdateTypeAction.IncidentUpdateType(callback, request, callback.Channel, callback.User)