* TP-47297 | Slack DM for incident reminder - A cron job to send list of open incidents to every users who are part of it along with their role in it * Delete common/util/config_util.go --------- Co-authored-by: Md Anees <md.anees@navi.com>
216 lines
6.7 KiB
Go
216 lines
6.7 KiB
Go
package util
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/slack-go/slack"
|
|
"github.com/slack-go/slack/socketmode"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/exp/slices"
|
|
"houston/logger"
|
|
"houston/model/incident"
|
|
"houston/model/severity"
|
|
"houston/model/team"
|
|
request "houston/service/request"
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
func RemoveDuplicate[T string | int](sliceList []T) []T {
|
|
allKeys := make(map[T]bool)
|
|
list := []T{}
|
|
for _, item := range sliceList {
|
|
if _, value := allKeys[item]; !value {
|
|
allKeys[item] = true
|
|
list = append(list, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
// GetTimeElapsedInDaysAndHours - returns number of days and hours elapsed since the timestamp passed in argument
|
|
func GetTimeElapsedInDaysAndHours(timestamp time.Time) (int, int) {
|
|
// convert to IST time
|
|
locationName := "Asia/Kolkata"
|
|
location, err := time.LoadLocation(locationName)
|
|
if err != nil {
|
|
logger.Error(fmt.Sprintf("failed to load location for locationName %s", locationName))
|
|
}
|
|
timestampInIST := timestamp.In(location)
|
|
currentTimeInIST := time.Now().In(location)
|
|
// Calculate the time difference
|
|
timeDiff := currentTimeInIST.Sub(timestampInIST)
|
|
|
|
// Convert the duration into days and hours
|
|
days := int(timeDiff.Hours() / 24)
|
|
hours := int(timeDiff.Hours()) % 24
|
|
return days, hours
|
|
}
|
|
|
|
// Difference : finds difference of two slices and returns a new slice
|
|
func Difference(s1, s2 []string) []string {
|
|
combinedSlice := append(s1, s2...)
|
|
m := make(map[string]int)
|
|
for _, v := range combinedSlice {
|
|
if _, ok := m[v]; ok {
|
|
// remove element later as it exist in both slice.
|
|
m[v] += 1
|
|
continue
|
|
}
|
|
// new entry, add in map!
|
|
m[v] = 1
|
|
}
|
|
var result []string
|
|
for k, v := range m {
|
|
if v == 1 {
|
|
result = append(result, k)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func Intersection(s1, s2 []string) (inter []string) {
|
|
hash := make(map[string]bool)
|
|
for _, e := range s1 {
|
|
hash[e] = true
|
|
}
|
|
for _, e := range s2 {
|
|
// If elements present in the hashmap then append intersection list.
|
|
if hash[e] {
|
|
inter = append(inter, e)
|
|
}
|
|
}
|
|
//Remove dups from slice.
|
|
inter = RemoveDuplicate(inter)
|
|
return
|
|
}
|
|
|
|
// Contains checks if the given string exists on the slice of string and returns boolean
|
|
func Contains[S ~[]E, E comparable](s S, v E) bool {
|
|
return slices.Contains(s, v)
|
|
}
|
|
|
|
func GetColorBySeverity(severityId uint) string {
|
|
switch severityId {
|
|
case 1:
|
|
return "#fc3838"
|
|
case 2:
|
|
return "#fc9338"
|
|
case 3:
|
|
return "#ebfa1b"
|
|
default:
|
|
return "#7288db"
|
|
}
|
|
}
|
|
|
|
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 {
|
|
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set status to %s", userId, updatedStatus), false)
|
|
_, _, errMessage := client.PostMessage(channelId, msgOption)
|
|
return errMessage
|
|
}
|
|
|
|
func PostIncidentTypeUpdateMessage(
|
|
userId, updatedTeam, severity, severityDesc, incidentName, incidentTitle, channelId string, client *socketmode.Client) error {
|
|
txt := fmt.Sprintf("<@%s> *>* set the channel topic: *%s · %s (%s) %s* | %s", userId, updatedTeam, severity, severityDesc, incidentName, incidentTitle)
|
|
att := slack.Attachment{
|
|
Text: txt,
|
|
Color: "#808080", // Grey color code
|
|
MarkdownIn: []string{"text"}, // Define which fields support markdown
|
|
}
|
|
_, _, errMessage := client.PostMessage(channelId, slack.MsgOptionAttachments(att))
|
|
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 PostIncidentCustomerDataUpdateMessage(metadata request.CreateIncidentMetaData, userId, channelId string, client *socketmode.Client) error {
|
|
marshalledMetadata, _ := json.Marshal([]request.CreateIncidentMetaData{metadata})
|
|
msgOption, err := BuildSlackTextMessageFromMetaData(marshalledMetadata, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _, errMessage := client.PostMessage(channelId, msgOption)
|
|
return errMessage
|
|
}
|
|
|
|
func RemoveDuplicateStr(strSlice []string) []string {
|
|
allKeys := make(map[string]bool)
|
|
list := []string{}
|
|
for _, item := range strSlice {
|
|
if _, value := allKeys[item]; !value {
|
|
allKeys[item] = true
|
|
list = append(list, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
func PostIncidentSeverityEscalationHeadsUpMessage(severities *[]severity.SeverityEntity, incidentEntity *incident.IncidentEntity, teamEntity *team.TeamEntity, client *socketmode.Client) {
|
|
fromSeverityName := getSeverityById(severities, incidentEntity.SeverityId)
|
|
toSeverityName := getSeverityById(severities, incidentEntity.SeverityId-1)
|
|
daysToEscalation := calculateDifferenceInDays(incidentEntity.SeverityTat, time.Now())
|
|
teamSlackChannel := teamEntity.WebhookSlackChannel
|
|
if daysToEscalation != 0 {
|
|
msgOption := slack.MsgOptionText(fmt.Sprintf("This incident will be auto-escalated to `%v` in `%v day(s)`", toSeverityName, daysToEscalation), false)
|
|
_, _, err := client.PostMessage(incidentEntity.SlackChannel, msgOption)
|
|
if err != nil {
|
|
logger.Info(fmt.Sprintf("Error posting message to incident channel for incident %v", incidentEntity.IncidentName), zap.Error(err))
|
|
return
|
|
}
|
|
if teamSlackChannel != "" {
|
|
msgOption := slack.MsgOptionText(fmt.Sprintf("<#%s> (`%v`) will be auto-escalated to `%v` in `%v day(s)`", incidentEntity.SlackChannel, fromSeverityName, toSeverityName, daysToEscalation), false)
|
|
_, _, err := client.PostMessage(teamSlackChannel, msgOption)
|
|
if err != nil {
|
|
logger.Info(fmt.Sprintf("Error posting message to team channel for incident %v", incidentEntity.IncidentName), zap.Error(err))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func getSeverityById(severities *[]severity.SeverityEntity, severityId uint) string {
|
|
for _, severityEntity := range *severities {
|
|
if severityEntity.ID == severityId {
|
|
return severityEntity.Name
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func calculateDifferenceInDays(fromTime, toTime time.Time) int {
|
|
fromDate := time.Date(fromTime.Year(), fromTime.Month(), fromTime.Day(), 0, 0, 0, 0, time.UTC)
|
|
toDate := time.Date(toTime.Year(), toTime.Month(), toTime.Day(), 0, 0, 0, 0, time.UTC)
|
|
return int(math.Abs(toDate.Sub(fromDate).Hours() / 24))
|
|
}
|
|
|
|
func PostMessageToIncidentChannel(message string, channelId string, client *socketmode.Client) error {
|
|
msgOption := slack.MsgOptionText(message, false)
|
|
_, _, errMessage := client.PostMessage(channelId, msgOption)
|
|
return errMessage
|
|
}
|
|
|
|
func ConvertSliceToMapOfString(input []string) map[string]string {
|
|
output := make(map[string]string)
|
|
for _, item := range input {
|
|
output[item] = item
|
|
}
|
|
return output
|
|
}
|