Tp 28766 (#69)

* TP-28766 | Houston summary

* TP-28766 | cron changes

* TP-28766 | update

* TP-28766 | changes in format

* TP-28766 | Team metric

* TP-28766 | change config

* TP-28766 | change config

* TP-28766 | team metric interval change
This commit is contained in:
Abhijeet Gupta
2023-05-17 18:42:50 +05:30
committed by GitHub Enterprise
parent cab34d6379
commit 1945df5fa5
7 changed files with 199 additions and 2 deletions

BIN
cmd/cmd Executable file

Binary file not shown.

View File

@@ -20,6 +20,19 @@ 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 {
msgOption := slack.MsgOptionText(fmt.Sprintf("<@%s> > set status to %s", userId, updatedStatus), false)
_, _, errMessage := client.PostMessage(channelId, msgOption)
@@ -54,4 +67,4 @@ func RemoveDuplicateStr(strSlice []string) []string {
}
}
return list
}
}

View File

@@ -18,6 +18,9 @@ cron.job.update.incident.interval=CRON_JOB_UPDATE_INCIDENT_INTERVAL
cron.job.lock.default.time.in.sec=CRON_JOB_LOCK_DEFAULT_TIME_IN_SEC
cron.job.lock.ticker.time.in.sec=CRON_JOB_LOCK_TICKER_TIME_IN_SEC
cron.job.name=CRON_JOB_NAME
cron.job.team.metric=CRON_JOB_TEAM_METRIC
cron.job.team.metric.interval=CRON_JOB_TEAM_METRIC_INTERVAL
team.metric.update.channel=TEAM_METRIC_UPDATE_CHANNEL_ID
#incidents
incidents.show.limit=INCIDENTS_SHOW_LIMIT

View File

@@ -5,6 +5,7 @@ import (
"strconv"
"time"
"houston/common/util"
"houston/internal/processor/action"
"houston/model/incident"
"houston/model/severity"
@@ -21,14 +22,24 @@ import (
func RunJob(socketModeClient *socketmode.Client, db *gorm.DB, logger *zap.Logger, incidentService *incident.Repository, severityService *severity.Repository, teamService *team.Repository, shedlockService *shedlock.Repository) {
//HOUSTON ESCALATE
shedlockConfig := NewLockerDbWithLockTime(viper.GetInt("cron.job.lock.default.time.in.sec"))
err := shedlockConfig.AddFun(viper.GetString("cron.job.name"), viper.GetString("cron.job.update.incident.interval"), shedlockService, func() {
UpdateIncidentByCronJob(socketModeClient, db, logger, incidentService, teamService, severityService, viper.GetString("cron.job.name"))
})
if err != nil {
logger.Error(err.Error())
logger.Error("HOUSTON_ESCALATE error: " + err.Error())
}
//HOUSTON DAILY TEAM UPDATE
err = shedlockConfig.AddFun(viper.GetString("cron.job.team.metric"), viper.GetString("cron.job.team.metric.interval"), shedlockService, func() {
postTeamMetrics(socketModeClient, db, logger, incidentService, teamService, severityService, viper.GetString("cron.job.team.metric"))
})
if err != nil {
logger.Error("HOUSTON_TEAM_METRICS error :" + err.Error())
}
shedlockConfig.Start()
}
@@ -128,3 +139,142 @@ func updatingSevForEachInc(logger *zap.Logger, incidents []incident.IncidentEnti
logger.Error("PostMessage failed for cronJob ", zap.Error(errMessage), zap.Int("incidentId", int(incidents[i].ID)))
}
}
func postTeamMetrics(socketModeClient *socketmode.Client, db *gorm.DB, logger *zap.Logger, incidentService *incident.Repository, teamService *team.Repository, severityService *severity.Repository, name string) {
fmt.Println("Running Team Metrics cron at", time.Now().Format(time.RFC3339))
defer func() {
if r := recover(); r != nil {
logger.Error(fmt.Sprintf("Exception occurred in cron: %v", r.(error)))
}
}()
teams, err := teamService.GetAllActiveTeams()
if err != nil {
logger.Error("GetAllActiveTeams error", zap.Error(err))
return
}
teamsList := *teams
//Do not need to fetch Severity every time, hence keeping a map outside.
incidentSeverityList, err := severityService.GetAllActiveSeverity()
if err != nil || incidentSeverityList == nil {
logger.Error("GetAllActiveSeverity error in cron Job",
zap.String("cron_name", name), zap.Error(err))
return
}
severityMap := convertSeverityListToMap(*incidentSeverityList)
for i := 0; i < len(teamsList); i++ {
incidents, err := incidentService.GetIncidentsByTeamIdAndNotResolved(teamsList[i].ID)
if err != nil {
logger.Error("GetIncidentsByTeamIdAndNotResolved error in cron Job",
zap.String("cron_name", name), zap.Error(err))
continue
}
incidentsList := *incidents
list := make([]incident.SlackChannelWithResponderId, 0, len(incidentsList))
for j := 0; j < len(incidentsList); j++ {
//RESPONDER
incidentRole, err := incidentService.GetIncidentRoleByIncidentIdAndRole(incidentsList[j].ID, incident.Responder)
if err != nil {
logger.Error("GetIncidentRoleByIncidentIdAndRole error in cron Job",
zap.String("cron_name", name), zap.Error(err))
continue
}
// map to pojo and to list
obj := incident.SlackChannelWithResponderId{
SlackChannel: incidentsList[j].SlackChannel,
ResponderId: incidentRole.AssignedTo,
CreatedAt: incidentsList[j].CreatedAt,
Severity: severityMap[incidentsList[j].SeverityId],
ManagerId: teamsList[i].ManagerHandle,
}
// Appending the obj to the list
list = append(list, obj)
}
//Post message
teamName := builderTeamNameHeader(teamsList[i].Name)
openIncidentCount := buildOpenIncidentText(len(incidentsList))
onCallAndManagerBlock := buildOnCallAndManagerBlock(teamsList[i].OncallHandle, teamsList[i].ManagerHandle)
blocks := IncidentMetricBlock(teamName, openIncidentCount, onCallAndManagerBlock)
//On the basis of no of incidents, change colour
color := util.GetColourByOpenIncidents(len(incidentsList))
att := slack.Attachment{Blocks: blocks, Color: color}
_, _, err = socketModeClient.PostMessage(viper.GetString("team.metric.update.channel"), slack.MsgOptionAttachments(att))
text := ""
for index := 0; index < len(list); index++ {
// Calculate the time difference
currentTime := time.Now()
duration := 5*time.Hour + 30*time.Minute
newTime := currentTime.Add(duration)
timeDiff := newTime.Sub(list[index].CreatedAt)
// Convert the duration into days and hours
days := int(timeDiff.Hours() / 24)
hours := int(timeDiff.Hours()) % 24
if list[index].ResponderId == "" && list[index].ManagerId != "" {
text += fmt.Sprintf("<#%s>'s *Severity* is `%s` assigned to *Manager* <@%s>. *Open Since*- `%dd%dhr` \n", list[index].SlackChannel, list[index].Severity, list[index].ManagerId, days, hours)
} else if list[index].ResponderId != "" {
text += fmt.Sprintf("<#%s>'s *Severity* is `%s` assigned to *Responder* <@%s>. *Open Since*- `%dd%dhr` \n", list[index].SlackChannel, list[index].Severity, list[index].ResponderId, days, hours)
} else if list[index].ResponderId == "" && list[index].ManagerId == "" {
text += fmt.Sprintf("<#%s>'s *Severity* is `%s` assigned to *No-One*. *Open Since*- `%dd%dhr` \n", list[index].SlackChannel, list[index].Severity, days, hours)
}
}
if text != "" {
msgOption := slack.MsgOptionText(text, false)
_, _, errMessage := socketModeClient.PostMessage(viper.GetString("team.metric.update.channel"), msgOption)
if errMessage != nil {
logger.Error("PostMessage failed for cronJob ", zap.Error(errMessage))
}
}
}
}
func convertSeverityListToMap(severities []severity.SeverityEntity) map[uint]string {
severityMap := make(map[uint]string)
for _, severity := range severities {
severityMap[severity.ID] = severity.Name
}
return severityMap
}
func IncidentMetricBlock(teamName *slack.HeaderBlock, incidentOpenCount *slack.SectionBlock, onCallAndManagerBlock *slack.SectionBlock) slack.Blocks {
return slack.Blocks{
BlockSet: []slack.Block{
teamName,
incidentOpenCount,
onCallAndManagerBlock,
},
}
}
func builderTeamNameHeader(teamName string) *slack.HeaderBlock {
headerText := slack.NewTextBlockObject(slack.PlainTextType, teamName, true, false)
headerSection := slack.NewHeaderBlock(headerText)
return headerSection
}
func buildOpenIncidentText(count int) *slack.SectionBlock {
sectionBlock := slack.NewTextBlockObject(slack.PlainTextType, fmt.Sprintf("Open Incidents: %d", count), true, false)
headerSection := slack.NewSectionBlock(sectionBlock, nil, nil)
return headerSection
}
func buildOnCallAndManagerBlock(onCallHandle string, managerHandle string) *slack.SectionBlock {
fields := []*slack.TextBlockObject{
slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*OnCall*\n<@%s>", onCallHandle), false, false),
slack.NewTextBlockObject("mrkdwn", fmt.Sprintf("*Manager*\n<@%s>", managerHandle), false, false),
}
block := slack.NewSectionBlock(nil, fields, nil)
return block
}

View File

@@ -380,3 +380,25 @@ func (r *Repository) FindIncidentsByNotResolvedStatusAndGreaterSeverityTatThanCu
return incidentEntity, nil
}
func (r *Repository) GetIncidentsByTeamIdAndNotResolved(team_id uint) (*[]IncidentEntity, error) {
var incidentEntity []IncidentEntity
result := r.gormClient.Order("severity_id").Find(&incidentEntity, "team_id = ? and status <> ?", team_id, 4)
if result.Error != nil {
return nil, result.Error
}
return &incidentEntity, nil
}
func (r *Repository) GetIncidentRoleByIncidentIdAndRole(incident_id uint, role string) (*IncidentRoleEntity, error) {
var incidentRoleEntity IncidentRoleEntity
result := r.gormClient.Find(&incidentRoleEntity, "incident_id = ? and role = ? and deleted_at IS NULL", incident_id, role)
if result.Error != nil {
return nil, result.Error
}
return &incidentRoleEntity, nil
}

View File

@@ -50,3 +50,11 @@ type AddIncidentStatusRequest struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
}
type SlackChannelWithResponderId struct {
SlackChannel string `json:"slack_channel,omitempty"`
ResponderId string `json:"responder_id,omitempty"`
CreatedAt time.Time
Severity string
ManagerId string
}

View File

@@ -13,6 +13,7 @@ type TeamEntity struct {
OncallHandle string `gorm:"column:oncall_handle"`
Active bool `gorm:"column:active"`
WebhookSlackChannel string `gorm:"column:webhook_slack_channel"`
ManagerHandle string `gorm:"column:manager_handle"`
}
func (TeamEntity) TableName() string {