Skip to content

Commit

Permalink
UI updates (#19)
Browse files Browse the repository at this point in the history
* UI updates

* update

Co-authored-by: Karl-Johan Grahn <[email protected]>
  • Loading branch information
karl-johan-grahn and Karl-Johan Grahn authored Nov 4, 2021
1 parent 6c8cf59 commit 1ef2833
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 43 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.10.0] - 2021-11-03
### Updates
- Update UI such as update wording, add hints for responder and commander

## [0.9.0] - 2021-11-02
### Updates
- Update date handling
Expand Down
6 changes: 4 additions & 2 deletions bot/active.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
"ArchiveIncidentChannel": "Archive incident channel",
"Cancel": "Cancel",
"Commander": "Commander",
"CommanderHint": "The incident commander coordinates, communicates, and controls the response",
"DeclareIncident": "Declare incident",
"DeclareNewIncident": "Declare a new incident",
"Environment": "Environment",
"HelpMessage": "These are the available commands:\n> `/devopsbot help` - Get this help\n> `/devopsbot incident` - Declare an incident\n> `/devopsbot resolve` - Resolve an incident",
"Incident": "Incident",
"IncidentChannelNamePattern": "Choose a channel that starts with 'inc_'",
"IncidentCreationDescription": "This will create a new incident Slack channel, and notify {{.broadcastChannel}} about the incident",
"IncidentCreationDescription": "This will create a new incident Slack channel, and notify {{.broadcastChannel}} about the incident. This incident response system is based on the Incident Command System (ICS).",
"IncidentName": "Incident name",
"IncidentNameHint": "Incident names may only contain lowercase letters, numbers, hyphens, and underscores, and must be 60 characters or less",
"IncidentSummary": "Incident summary",
"Invitees": "Invitees",
"No": "No",
"Region": "Region",
Expand All @@ -19,8 +21,8 @@
"ResolveIncident": "Resolve incident",
"ResolveIncidentDescription": "This will resolve the chosen incident and notify {{.broadcastChannel}} about the resolution",
"Responder": "Responder",
"ResponderHint": "The responder leads the work of resolving the incident",
"SecurityIncident": "Security Incident",
"SecurityIncidentLabel": "Mark to make incident channel private",
"Summary": "Summary",
"Yes": "Yes"
}
18 changes: 15 additions & 3 deletions bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (h *botHandler) cmdIncident(ctx context.Context, w http.ResponseWriter, cmd
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "IncidentCreationDescription",
Other: "This will create a new incident Slack channel, and notify {{.broadcastChannel}} about the incident"},
Other: "This will create a new incident Slack channel, and notify {{.broadcastChannel}} about the incident. This incident response system is based on the Incident Command System (ICS)."},
TemplateData: map[string]string{"broadcastChannel": fmt.Sprintf("<#%s>", h.opts.BroadcastChannelID)},
}), false, false)
contextBlock := slack.NewContextBlock("context", contextText)
Expand Down Expand Up @@ -217,6 +217,12 @@ func (h *botHandler) cmdIncident(ctx context.Context, w http.ResponseWriter, cmd
}), false, false)
responderOption := slack.NewOptionsSelectBlockElement(slack.OptTypeUser, responderText, "incident_responder")
responderBlock := slack.NewInputBlock("incident_responder", responderText, responderOption)
responderBlock.Hint = slack.NewTextBlockObject(slack.PlainTextType,
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ResponderHint",
Other: "The responder leads the work of resolving the incident"},
}), false, false)

commanderText := slack.NewTextBlockObject(slack.PlainTextType,
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
Expand All @@ -226,6 +232,12 @@ func (h *botHandler) cmdIncident(ctx context.Context, w http.ResponseWriter, cmd
}), false, false)
commanderOption := slack.NewOptionsSelectBlockElement(slack.OptTypeUser, commanderText, "incident_commander")
commanderBlock := slack.NewInputBlock("incident_commander", commanderText, commanderOption)
commanderBlock.Hint = slack.NewTextBlockObject(slack.PlainTextType,
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "CommanderHint",
Other: "The incident commander coordinates, communicates, and controls the response"},
}), false, false)

envTxt := slack.NewTextBlockObject(slack.PlainTextType,
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
Expand Down Expand Up @@ -262,8 +274,8 @@ func (h *botHandler) cmdIncident(ctx context.Context, w http.ResponseWriter, cmd
summaryText := slack.NewTextBlockObject(slack.PlainTextType,
h.opts.Localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Summary",
Other: "Summary"},
ID: "IncidentSummary",
Other: "Incident summary"},
}), false, false)
summaryElement := slack.NewPlainTextInputBlockElement(summaryText, "incident_summary")
// Set an arbitrary max length to avoid prose summary
Expand Down
59 changes: 21 additions & 38 deletions bot/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ func (h *botHandler) declareIncident(ctx context.Context, payload *slack.Interac
incidentSummary: payload.View.State.Values["incident_summary"]["incident_summary"].Value,
incidentDeclarer: payload.User.ID,
}
// Add incident responder and incident commander to the people to be invited to the incident channel
inputParams.incidentInvitees = append(inputParams.incidentInvitees, inputParams.incidentResponder, inputParams.incidentCommander)

// Create channel - should be done here because it will update the modal if there are errors
incidentChannel, err := h.slackClient.CreateConversationContext(ctx, incidentChannelName, inputParams.incidentSecurityRelated)
Expand All @@ -214,11 +216,6 @@ func (h *botHandler) declareIncident(ctx context.Context, payload *slack.Interac
}, w)
}

if err := h.sendMessage(ctx, h.opts.BroadcastChannelID, slack.MsgOptionPostEphemeral(inputParams.incidentDeclarer),
slack.MsgOptionText(fmt.Sprintf("Started work on declaring the incident <#%s>", incidentChannel.ID), false)); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return err
}
w.WriteHeader(http.StatusAccepted)

// Do the rest via goroutine
Expand All @@ -239,15 +236,17 @@ func (h *botHandler) doIncidentTasks(ctx context.Context, params *inputParams, i
securityMessage = ""
}
// Set channel purpose and topic
overview := fmt.Sprintf("*Summary:* %s\n"+
overview := fmt.Sprintf("*Incident channel*\n"+
"*Incident summary:* %s\n"+
"*Environment affected:* %s\n"+
"*Region affected:* %s\n"+
"*Responder:* <@%s>\n"+
"*Commander:* <@%s>\n\n"+
"*Commander:* <@%s>\n"+
"*Broadcast channel:* <#%s>\n\n"+
"Declared by: <@%s>\n"+
securityMessage,
params.incidentSummary, params.incidentEnvironmentsAffected, params.incidentRegionsAffected,
params.incidentResponder, params.incidentCommander, params.incidentDeclarer)
params.incidentResponder, params.incidentCommander, h.opts.BroadcastChannelID, params.incidentDeclarer)
if _, err := h.slackClient.SetPurposeOfConversationContext(ctx, incidentChannel.ID, overview); err != nil {
if sendErr := h.sendMessage(ctx, h.opts.BroadcastChannelID, slack.MsgOptionPostEphemeral(params.incidentDeclarer),
slack.MsgOptionText(fmt.Sprintf("Failed to set purpose for incident channel: %s", err.Error()), false)); sendErr != nil {
Expand Down Expand Up @@ -276,7 +275,7 @@ func (h *botHandler) doIncidentTasks(ctx context.Context, params *inputParams, i
// Inform about incident
if err := h.sendMessage(ctx, h.opts.BroadcastChannelID,
slack.MsgOptionText(fmt.Sprintf(":rotating_siren: An incident has been declared by <@%s>\n"+
"*Summary:* %s\n"+
"*Incident summary:* %s\n"+
"*Environment affected:* %s\n"+
"*Region affected:* %s\n"+
"*Responder:* <@%s>\n"+
Expand All @@ -289,31 +288,27 @@ func (h *botHandler) doIncidentTasks(ctx context.Context, params *inputParams, i
log.Error().Err(err).Msg(sendError)
return
}
// Send message about starting slack call
// Send message about starting a video call for live troubleshooting
if err := h.sendMessage(ctx, incidentChannel.ID,
slack.MsgOptionText(fmt.Sprintf("IC <@%s>: Start a Teams call with the command `/teams-calls meeting %s`", params.incidentCommander, params.incidentChannelName), false)); err != nil {
slack.MsgOptionText(fmt.Sprintf("IC <@%s>: Start an incident Teams call with the command `/teams-calls meeting %s` and invite the appropriate people", params.incidentCommander, params.incidentChannelName), false)); err != nil {
log.Error().Err(err).Msg(sendError)
return
}
// Send message about starting incident doc
// Send message about starting an incident document for postmortem
if err := h.sendMessage(ctx, incidentChannel.ID,
slack.MsgOptionText(fmt.Sprintf("IC <@%s>: Start the incident doc by using [this template](%s)", params.incidentCommander, h.opts.IncidentDocTemplateURL), false)); err != nil {
slack.MsgOptionText(fmt.Sprintf("IC <@%s>: Start the incident document by using <%s|this template>", params.incidentCommander, h.opts.IncidentDocTemplateURL), false)); err != nil {
log.Error().Err(err).Msg(sendError)
return
}
// Add channel reminder about updating about progress
if _, err := h.slackClient.AddChannelReminder(
incidentChannel.ID,
// Add channel reminder about updating progress
if _, err := h.slackClient.AddChannelReminder(incidentChannel.ID,
fmt.Sprintf("Reminder for IC <@%s>: Update progress about the incident in <#%s>", params.incidentCommander, h.opts.BroadcastChannelID),
"Every 30 min"); err != nil {
log.Error().Err(err).Msg(sendError)
return
}
// Inform about being done with declaring incident
if err := h.sendMessage(ctx, h.opts.BroadcastChannelID, slack.MsgOptionPostEphemeral(params.incidentDeclarer),
slack.MsgOptionText(fmt.Sprintf("Finished declaring the incident %s", params.incidentChannelName), false)); err != nil {
log.Error().Err(err).Msg(sendError)
return
if sendErr := h.sendMessage(ctx, incidentChannel.ID, slack.MsgOptionPostEphemeral(params.incidentDeclarer),
slack.MsgOptionText(fmt.Sprintf("Failed to add channel reminder: %s", err.Error()), false)); sendErr != nil {
log.Error().Err(sendErr).Msg(sendError)
return
}
}
}

Expand Down Expand Up @@ -346,11 +341,6 @@ func (h *botHandler) resolveIncident(ctx context.Context, payload *slack.Interac
incidentResolver: payload.User.ID,
}

if err := h.sendMessage(ctx, h.opts.BroadcastChannelID, slack.MsgOptionPostEphemeral(resolveParams.incidentResolver),
slack.MsgOptionText(fmt.Sprintf("Started work on resolving the incident <#%s>", incChannel.Name), false)); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return err
}
w.WriteHeader(http.StatusAccepted)

// Do the rest via goroutine
Expand All @@ -362,27 +352,20 @@ func (h *botHandler) resolveIncident(ctx context.Context, payload *slack.Interac

func (h *botHandler) doResolveTasks(ctx context.Context, params *resolveParams) {
log := zerolog.Ctx(ctx)
const sendError = "Could not send failure message"
// Inform about resolution
if err := h.sendMessage(ctx, h.opts.BroadcastChannelID,
slack.MsgOptionText(fmt.Sprintf(":white_check_mark: The incident <#%s> has been resolved!\n"+
"*Resolution:* %s",
params.incidentChannel, params.incidentResolution), false)); err != nil {
log.Error().Err(err).Msg(sendError)
log.Error().Err(err).Msg("Could not send failure message")
return
}
if params.incidentArchive {
if err := h.slackClient.ArchiveConversationContext(ctx, params.incidentChannel); err != nil {
log.Error().Err(err).Msg(sendError)
log.Error().Err(err).Msg("Could not archive channel")
return
}
}
// Inform about being done with declaring incident
if err := h.sendMessage(ctx, h.opts.BroadcastChannelID, slack.MsgOptionPostEphemeral(params.incidentResolver),
slack.MsgOptionText(fmt.Sprintf("Finished resolving the incident <#%s>", params.incidentChannel), false)); err != nil {
log.Error().Err(err).Msg(sendError)
return
}
}

// sendMessage - a simplified way to send a message
Expand Down

0 comments on commit 1ef2833

Please sign in to comment.