TP-38301 | ading resolution text modal and action (#120)

* TP-38301 | ading resolution text modal and action

* TP-38301 | ading resolution text processor
This commit is contained in:
Shashank Shekhar
2023-08-16 13:43:07 +05:30
committed by GitHub
parent 89df2f8300
commit 147958cc1a
7 changed files with 265 additions and 89 deletions

View File

@@ -3,31 +3,33 @@ package util
type BlockActionType string
const (
StartIncident BlockActionType = "start_incident"
ShowIncidents = "show_incidents"
Incident = "incident"
Tags = "tags"
AssignIncidentRole = "assign_incident_role"
ResolveIncident = "resolve_incident"
SetIncidentStatus = "set_incident_status"
SetIncidentType = "set_incident_type"
SetIncidentSeverity = "set_incident_severity"
SetIncidentTitle = "set_incident_title"
SetIncidentDescription = "set_incident_description"
AddTags = "add_tags"
ShowTags = "show_tags"
RemoveTag = "remove_tags"
StartIncident BlockActionType = "start_incident"
ShowIncidents = "show_incidents"
Incident = "incident"
Tags = "tags"
AssignIncidentRole = "assign_incident_role"
ResolveIncident = "resolve_incident"
SetIncidentStatus = "set_incident_status"
SetIncidentType = "set_incident_type"
SetIncidentSeverity = "set_incident_severity"
SetIncidentTitle = "set_incident_title"
SetIncidentDescription = "set_incident_description"
SetIncidentResolutionText = "set_incident_resolution_text"
AddTags = "add_tags"
ShowTags = "show_tags"
RemoveTag = "remove_tags"
)
type ViewSubmissionType string
const (
StartIncidentSubmit ViewSubmissionType = "start_incident_submit"
AssignIncidentRoleSubmit = "assign_incident_role_submit"
SetIncidentStatusSubmit = "set_incident_status_submit"
SetIncidentTitleSubmit = "set_incident_title_submit"
SetIncidentDescriptionSubmit = "set_incident_description_submit"
SetIncidentSeveritySubmit = "set_incident_severity_submit"
SetIncidentTypeSubmit = "set_incident_type_submit"
UpdateTagSubmit = "updateTagSubmit"
StartIncidentSubmit ViewSubmissionType = "start_incident_submit"
AssignIncidentRoleSubmit = "assign_incident_role_submit"
SetIncidentStatusSubmit = "set_incident_status_submit"
SetIncidentTitleSubmit = "set_incident_title_submit"
SetIncidentDescriptionSubmit = "set_incident_description_submit"
SetIncidentSeveritySubmit = "set_incident_severity_submit"
SetIncidentTypeSubmit = "set_incident_type_submit"
UpdateTagSubmit = "updateTagSubmit"
SetIncidentResolutionTextSubmit = "set_incident_resolution_text_submit"
)

View File

@@ -6,6 +6,7 @@ import (
"houston/model/severity"
"houston/model/tag"
"houston/model/team"
"strings"
"time"
"github.com/slack-go/slack"
@@ -49,6 +50,7 @@ func (irp *ResolveIncidentAction) IncidentResolveProcess(callback slack.Interact
irp.logger.Error(fmt.Sprintf("failure while getting tags for incident id: %v", incidentEntity.ID))
return
}
// check if tags are required to be set
var flag = true
for _, t := range *tags {
if t.Optional == false {
@@ -77,42 +79,53 @@ func (irp *ResolveIncidentAction) IncidentResolveProcess(callback slack.Interact
}
}
if flag == true {
now := time.Now()
incidentEntity.Status = incidentStatusEntity.ID
incidentEntity.EndTime = &now
err = irp.incidentService.UpdateIncident(incidentEntity)
if err != nil {
irp.logger.Error("failed to update incident to resolve state",
zap.String("channel", channelId),
zap.String("user_id", callback.User.ID), zap.Error(err))
return
}
irp.logger.Info("successfully resolved the incident",
zap.String("channel", channelId),
zap.String("user_id", callback.User.ID))
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `houston set status to %s`", callback.User.ID,
incident.Resolved), false)
_, _, errMessage := irp.client.PostMessage(callback.Channel.ID, msgOption)
if errMessage != nil {
irp.logger.Error("post response failed for ResolveIncident", zap.Error(errMessage))
return
}
msgUpdate := NewIncidentChannelMessageUpdateAction(irp.client, irp.logger, irp.incidentService, irp.teamRepository, irp.severityRepository)
msgUpdate.ProcessAction(incidentEntity.SlackChannel)
if incidentEntity.SeverityId != 1 && incidentEntity.SeverityId != 2 {
irp.client.ArchiveConversation(channelId)
}
} else {
msgOption := slack.MsgOptionText(fmt.Sprintf("`Please set tag value`"), false)
// check if resolution text is set
if strings.TrimSpace(incidentEntity.ResolutionText) == "" {
msgOption := slack.MsgOptionText(fmt.Sprintf("`Please set incident resolution text`"), false)
_, errMessage := irp.client.PostEphemeral(callback.Channel.ID, callback.User.ID, msgOption)
if errMessage != nil {
irp.logger.Error("post response failed for ResolveIncident", zap.Error(errMessage))
return
}
} else {
// check if all tags are set
if flag == true {
now := time.Now()
incidentEntity.Status = incidentStatusEntity.ID
incidentEntity.EndTime = &now
err = irp.incidentService.UpdateIncident(incidentEntity)
if err != nil {
irp.logger.Error("failed to update incident to resolve state",
zap.String("channel", channelId),
zap.String("user_id", callback.User.ID), zap.Error(err))
return
}
irp.logger.Info("successfully resolved the incident",
zap.String("channel", channelId),
zap.String("user_id", callback.User.ID))
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `houston set status to %s`", callback.User.ID,
incident.Resolved), false)
_, _, errMessage := irp.client.PostMessage(callback.Channel.ID, msgOption)
if errMessage != nil {
irp.logger.Error("post response failed for ResolveIncident", zap.Error(errMessage))
return
}
msgUpdate := NewIncidentChannelMessageUpdateAction(irp.client, irp.logger, irp.incidentService, irp.teamRepository, irp.severityRepository)
msgUpdate.ProcessAction(incidentEntity.SlackChannel)
if incidentEntity.SeverityId != 1 && incidentEntity.SeverityId != 2 {
irp.client.ArchiveConversation(channelId)
}
} else {
msgOption := slack.MsgOptionText(fmt.Sprintf("`Please set tag value`"), false)
_, errMessage := irp.client.PostEphemeral(callback.Channel.ID, callback.User.ID, msgOption)
if errMessage != nil {
irp.logger.Error("post response failed for ResolveIncident", zap.Error(errMessage))
return
}
}
}
var payload interface{}

View File

@@ -0,0 +1,99 @@
package action
import (
"fmt"
"houston/internal/processor/action/view"
"houston/model/incident"
"github.com/slack-go/slack"
"github.com/slack-go/slack/socketmode"
"go.uber.org/zap"
)
type IncidentUpdateResolutionTextAction struct {
client *socketmode.Client
logger *zap.Logger
incidentRepository *incident.Repository
}
func NewIncidentUpdateResolutionTextAction(client *socketmode.Client, logger *zap.Logger, incidentRepository *incident.Repository) *IncidentUpdateResolutionTextAction {
return &IncidentUpdateResolutionTextAction{
client: client,
logger: logger,
incidentRepository: incidentRepository,
}
}
func (idp *IncidentUpdateResolutionTextAction) IncidentUpdateResolutionTextRequestProcess(callback slack.InteractionCallback, request *socketmode.Request) {
result, err := idp.incidentRepository.FindIncidentByChannelId(callback.Channel.ID)
if err != nil {
idp.logger.Error("FindIncidentByChannelId error ",
zap.String("incident_slack_channel_id", callback.Channel.ID), zap.String("channel", callback.Channel.Name),
zap.String("user_id", callback.User.ID), zap.Error(err))
return
} else if result == nil {
idp.logger.Error("IncidentEntity not found ",
zap.String("incident_slack_channel_id", callback.Channel.ID), zap.String("channel", callback.Channel.Name),
zap.String("user_id", callback.User.ID), zap.Error(err))
return
}
modalRequest := view.BuildIncidentResolutionTextModal(callback.Channel, result.ResolutionText)
_, err = idp.client.OpenView(callback.TriggerID, modalRequest)
if err != nil {
idp.logger.Error("houston slackbot openview command for IncidentUpdateResolutionTextRequestProcess failed.",
zap.String("trigger_id", callback.TriggerID), zap.String("channel_id", callback.Channel.ID), zap.Error(err))
return
}
var payload interface{}
idp.client.Ack(*request, payload)
}
func (itp *IncidentUpdateResolutionTextAction) IncidentUpdateResolutionText(callback slack.InteractionCallback, request *socketmode.Request, channel slack.Channel, user slack.User) {
incidentEntity, err := itp.incidentRepository.FindIncidentByChannelId(callback.View.PrivateMetadata)
if err != nil {
itp.logger.Error("FindIncidentByChannelId 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 incidentEntity == nil {
itp.logger.Error("IncidentEntity not found ",
zap.String("incident_slack_channel_id", callback.Channel.ID), zap.String("channel", callback.Channel.Name),
zap.String("user_id", callback.User.ID), zap.Error(err))
return
}
incidentResolutionText := buildUpdateIncidentResolutionTextRequest(callback.View.State.Values)
incidentEntity.ResolutionText = incidentResolutionText
incidentEntity.UpdatedBy = user.ID
err = itp.incidentRepository.UpdateIncident(incidentEntity)
if err != nil {
itp.logger.Error("IncidentUpdateResolutionText error",
zap.String("incident_slack_channel_id", channel.ID), zap.String("channel", channel.Name),
zap.String("user_id", user.ID), zap.Error(err))
return
}
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> *>* `houston set resolution_text = to %s`", user.ID, incidentEntity.ResolutionText), false)
_, _, errMessage := itp.client.PostMessage(callback.View.PrivateMetadata, msgOption)
if errMessage != nil {
itp.logger.Error("post response failed for IncidentUpdateResolutionText", zap.Error(errMessage))
return
}
var payload interface{}
itp.client.Ack(*request, payload)
}
func buildUpdateIncidentResolutionTextRequest(blockActions map[string]map[string]slack.BlockAction) string {
var requestMap = make(map[string]string, 0)
for _, actions := range blockActions {
for actionID, a := range actions {
if a.Type == "plain_text_input" {
requestMap[actionID] = a.Value
}
}
}
return requestMap["incident_resolution_text"]
}

View File

@@ -0,0 +1,42 @@
package view
import (
"github.com/slack-go/slack"
"houston/common/util"
)
func BuildIncidentResolutionTextModal(channel slack.Channel, description string) slack.ModalViewRequest {
titleText := slack.NewTextBlockObject(slack.PlainTextType, "Set Resolution Text", false, false)
closeText := slack.NewTextBlockObject(slack.PlainTextType, "Close", false, false)
submitText := slack.NewTextBlockObject(slack.PlainTextType, "Submit", false, false)
headerText := slack.NewTextBlockObject("mrkdwn", "Incident Resolution Text", false, false)
headerSection := slack.NewSectionBlock(headerText, nil, nil)
incidentResolutionTextText := slack.NewTextBlockObject(slack.PlainTextType, "Incident Resolution Text", false, false)
incidentResolutionTextPlaceholder := slack.NewTextBlockObject(slack.PlainTextType, "Write incident resolution text", false, false)
incidentResolutionTextElement := slack.NewPlainTextInputBlockElement(incidentResolutionTextPlaceholder, "incident_resolution_text")
incidentResolutionTextElement.Multiline = true
incidentResolutionTextElement.InitialValue = description
incidentResolutionTextElement.MaxLength = 3000
incidentResolutionText := slack.NewInputBlock("Incident resolution text", incidentResolutionTextText, nil, incidentResolutionTextElement)
incidentResolutionText.Optional = true
blocks := slack.Blocks{
BlockSet: []slack.Block{
headerSection,
incidentResolutionText,
},
}
return slack.ModalViewRequest{
Type: slack.VTModal,
Title: titleText,
Close: closeText,
Submit: submitText,
Blocks: blocks,
PrivateMetadata: channel.ID,
CallbackID: util.SetIncidentResolutionTextSubmit,
}
}

View File

@@ -127,6 +127,13 @@ func incidentSectionBlock() *slack.SectionBlock {
},
Value: util.SetIncidentDescription,
},
{
Text: &slack.TextBlockObject{
Type: "plain_text",
Text: "Set Incident Resolution Text",
},
Value: util.SetIncidentResolutionText,
},
}
accessoryOption := &slack.Accessory{

View File

@@ -20,38 +20,40 @@ type interactiveEventProcessor interface {
}
type BlockActionProcessor struct {
logger *zap.Logger
socketModeClient *socketmode.Client
startIncidentBlockAction *action.StartIncidentBlockAction
showIncidentsAction *action.ShowIncidentsAction
assignIncidentAction *action.AssignIncidentAction
incidentResolveAction *action.ResolveIncidentAction
incidentUpdateAction *action.UpdateIncidentAction
incidentUpdateTypeAction *action.IncidentUpdateTypeAction
incidentUpdateSeverityAction *action.IncidentUpdateSevertityAction
incidentUpdateTitleAction *action.IncidentUpdateTitleAction
incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction
incidentUpdateTagsAction *action.IncidentUpdateTagsAction
incidentShowTagsAction *action.IncidentShowTagsAction
logger *zap.Logger
socketModeClient *socketmode.Client
startIncidentBlockAction *action.StartIncidentBlockAction
showIncidentsAction *action.ShowIncidentsAction
assignIncidentAction *action.AssignIncidentAction
incidentResolveAction *action.ResolveIncidentAction
incidentUpdateAction *action.UpdateIncidentAction
incidentUpdateTypeAction *action.IncidentUpdateTypeAction
incidentUpdateSeverityAction *action.IncidentUpdateSevertityAction
incidentUpdateTitleAction *action.IncidentUpdateTitleAction
incidentUpdateDescriptionAction *action.IncidentUpdateDescriptionAction
incidentUpdateTagsAction *action.IncidentUpdateTagsAction
incidentShowTagsAction *action.IncidentShowTagsAction
incidentUpdateResolutionTextAction *action.IncidentUpdateResolutionTextAction
}
func NewBlockActionProcessor(logger *zap.Logger, socketModeClient *socketmode.Client, incidentService *incident.Repository,
func NewBlockActionProcessor(logger *zap.Logger, socketModeClient *socketmode.Client, incidentRepository *incident.Repository,
teamService *team.Repository, severityService *severity.Repository, tagService *tag.Repository,
slackbotClient *slackbot.Client) *BlockActionProcessor {
return &BlockActionProcessor{
logger: logger,
socketModeClient: socketModeClient,
startIncidentBlockAction: action.NewStartIncidentBlockAction(socketModeClient, logger, teamService, severityService),
showIncidentsAction: action.ShowIncidentsProcessor(socketModeClient, logger, incidentService),
assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, logger, incidentService),
incidentResolveAction: action.NewIncidentResolveProcessor(socketModeClient, logger, incidentService, tagService, teamService, severityService),
incidentUpdateAction: action.NewIncidentUpdateAction(socketModeClient, logger, incidentService, tagService, teamService, severityService),
incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, logger, incidentService, teamService, severityService, slackbotClient),
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, logger, incidentService, severityService, teamService, slackbotClient),
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, logger, incidentService, teamService, severityService, slackbotClient),
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, logger, incidentService),
incidentUpdateTagsAction: action.NewIncidentUpdateTagsAction(socketModeClient, logger, incidentService, teamService, tagService),
incidentShowTagsAction: action.NewIncidentShowTagsProcessor(socketModeClient, logger, incidentService, tagService),
logger: logger,
socketModeClient: socketModeClient,
startIncidentBlockAction: action.NewStartIncidentBlockAction(socketModeClient, logger, teamService, severityService),
showIncidentsAction: action.ShowIncidentsProcessor(socketModeClient, logger, incidentRepository),
assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, logger, incidentRepository),
incidentResolveAction: action.NewIncidentResolveProcessor(socketModeClient, logger, incidentRepository, tagService, teamService, severityService),
incidentUpdateAction: action.NewIncidentUpdateAction(socketModeClient, logger, incidentRepository, tagService, teamService, severityService),
incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, logger, incidentRepository, teamService, severityService, slackbotClient),
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, logger, incidentRepository, severityService, teamService, slackbotClient),
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, logger, incidentRepository, teamService, severityService, slackbotClient),
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, logger, incidentRepository),
incidentUpdateTagsAction: action.NewIncidentUpdateTagsAction(socketModeClient, logger, incidentRepository, teamService, tagService),
incidentShowTagsAction: action.NewIncidentShowTagsProcessor(socketModeClient, logger, incidentRepository, tagService),
incidentUpdateResolutionTextAction: action.NewIncidentUpdateResolutionTextAction(socketModeClient, logger, incidentRepository),
}
}
@@ -132,6 +134,10 @@ func (bap *BlockActionProcessor) processIncidentCommands(callback slack.Interact
{
bap.incidentUpdateDescriptionAction.IncidentUpdateDescriptionRequestProcess(callback, request)
}
case util.SetIncidentResolutionText:
{
bap.incidentUpdateResolutionTextAction.IncidentUpdateResolutionTextRequestProcess(callback, request)
}
}
}
@@ -165,23 +171,25 @@ type ViewSubmissionProcessor struct {
incidentUpdateSeverityAction *action.IncidentUpdateSevertityAction
incidentUpdateTypeAction *action.IncidentUpdateTypeAction
incidentUpdateTagsAction *action.IncidentUpdateTagsAction
incidentUpdateResolutionText *action.IncidentUpdateResolutionTextAction
}
func NewViewSubmissionProcessor(logger *zap.Logger, socketModeClient *socketmode.Client, incidentService *incident.Repository,
func NewViewSubmissionProcessor(logger *zap.Logger, socketModeClient *socketmode.Client, incidentRepository *incident.Repository,
teamService *team.Repository, severityService *severity.Repository, tagService *tag.Repository,
slackbotClient *slackbot.Client) *ViewSubmissionProcessor {
return &ViewSubmissionProcessor{
logger: logger,
socketModeClient: socketModeClient,
incidentChannelMessageUpdateAction: action.NewIncidentChannelMessageUpdateAction(socketModeClient, logger, incidentService, teamService, severityService),
createIncidentAction: action.NewCreateIncidentProcessor(socketModeClient, logger, incidentService, teamService, severityService, slackbotClient),
assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, logger, incidentService),
updateIncidentAction: action.NewIncidentUpdateAction(socketModeClient, logger, incidentService, tagService, teamService, severityService),
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, logger, incidentService, teamService, severityService, slackbotClient),
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, logger, incidentService),
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, logger, incidentService, severityService, teamService, slackbotClient),
incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, logger, incidentService, teamService, severityService, slackbotClient),
incidentUpdateTagsAction: action.NewIncidentUpdateTagsAction(socketModeClient, logger, incidentService, teamService, tagService),
incidentChannelMessageUpdateAction: action.NewIncidentChannelMessageUpdateAction(socketModeClient, logger, incidentRepository, teamService, severityService),
createIncidentAction: action.NewCreateIncidentProcessor(socketModeClient, logger, incidentRepository, teamService, severityService, slackbotClient),
assignIncidentAction: action.NewAssignIncidentAction(socketModeClient, logger, incidentRepository),
updateIncidentAction: action.NewIncidentUpdateAction(socketModeClient, logger, incidentRepository, tagService, teamService, severityService),
incidentUpdateTitleAction: action.NewIncidentUpdateTitleAction(socketModeClient, logger, incidentRepository, teamService, severityService, slackbotClient),
incidentUpdateDescriptionAction: action.NewIncidentUpdateDescriptionAction(socketModeClient, logger, incidentRepository),
incidentUpdateSeverityAction: action.NewIncidentUpdateSeverityAction(socketModeClient, logger, incidentRepository, severityService, teamService, slackbotClient),
incidentUpdateTypeAction: action.NewIncidentUpdateTypeAction(socketModeClient, logger, incidentRepository, teamService, severityService, slackbotClient),
incidentUpdateTagsAction: action.NewIncidentUpdateTagsAction(socketModeClient, logger, incidentRepository, teamService, tagService),
incidentUpdateResolutionText: action.NewIncidentUpdateResolutionTextAction(socketModeClient, logger, incidentRepository),
}
}
@@ -226,6 +234,10 @@ func (vsp *ViewSubmissionProcessor) ProcessCommand(callback slack.InteractionCal
{
vsp.incidentUpdateTagsAction.IncidentUpdateTags(callback, request)
}
case util.SetIncidentResolutionTextSubmit:
{
vsp.incidentUpdateResolutionText.IncidentUpdateResolutionText(callback, request, callback.Channel, callback.User)
}
default:
{
return

View File

@@ -47,6 +47,7 @@ type IncidentEntity struct {
CreatedBy string `gorm:"column:created_by"`
UpdatedBy string `gorm:"column:updated_by"`
MetaData JSON `gorm:"column:meta_data"`
ResolutionText string `gorm:"column:resolution_text"`
}
func (IncidentEntity) TableName() string {