github.com/wtfutil/wtf@v0.43.0/modules/pagerduty/widget.go (about)

     1  package pagerduty
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"time"
     7  
     8  	"github.com/PagerDuty/go-pagerduty"
     9  	"github.com/rivo/tview"
    10  	"github.com/wtfutil/wtf/utils"
    11  	"github.com/wtfutil/wtf/view"
    12  )
    13  
    14  const (
    15  	onCallTimeAPILayout     = "2006-01-02T15:04:05Z"
    16  	onCallTimeDisplayLayout = "Jan 2, 2006"
    17  )
    18  
    19  type Widget struct {
    20  	view.TextWidget
    21  
    22  	settings *Settings
    23  }
    24  
    25  // NewWidget creates and returns an instance of PagerDuty widget
    26  func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget {
    27  	widget := Widget{
    28  		TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common),
    29  
    30  		settings: settings,
    31  	}
    32  
    33  	return &widget
    34  }
    35  
    36  /* -------------------- Exported Functions -------------------- */
    37  
    38  func (widget *Widget) Refresh() {
    39  	var onCalls []pagerduty.OnCall
    40  	var incidents []pagerduty.Incident
    41  
    42  	var err1 error
    43  	var err2 error
    44  
    45  	if widget.settings.showIncidents {
    46  		teamIDs := utils.ToStrs(widget.settings.teamIDs)
    47  		userIDs := utils.ToStrs(widget.settings.userIDs)
    48  		incidents, err2 = GetIncidents(widget.settings.apiKey, teamIDs, userIDs)
    49  	}
    50  
    51  	if widget.settings.showSchedules {
    52  		scheduleIDs := utils.ToStrs(widget.settings.scheduleIDs)
    53  		onCalls, err1 = GetOnCalls(widget.settings.apiKey, scheduleIDs)
    54  	}
    55  
    56  	var content string
    57  	wrap := false
    58  	if err1 != nil || err2 != nil {
    59  		wrap = true
    60  		if err1 != nil {
    61  			content += err1.Error()
    62  		}
    63  		if err2 != nil {
    64  			content += err2.Error()
    65  		}
    66  	} else {
    67  		widget.View.SetWrap(false)
    68  		content = widget.contentFrom(onCalls, incidents)
    69  	}
    70  
    71  	widget.Redraw(func() (string, string, bool) { return widget.CommonSettings().Title, content, wrap })
    72  }
    73  
    74  /* -------------------- Unexported Functions -------------------- */
    75  
    76  func (widget *Widget) contentFrom(onCalls []pagerduty.OnCall, incidents []pagerduty.Incident) string {
    77  	var str string
    78  
    79  	// Incidents
    80  
    81  	if widget.settings.showIncidents {
    82  		str += fmt.Sprintf("[%s] Incidents[white]\n", widget.settings.Colors.Subheading)
    83  
    84  		if len(incidents) > 0 {
    85  			for _, incident := range incidents {
    86  				str += fmt.Sprintf("\n [%s]%s[white]\n", widget.settings.Colors.Label, tview.Escape(incident.Summary))
    87  				str += fmt.Sprintf("     Status: %s\n", incident.Status)
    88  				str += fmt.Sprintf("    Service: %s\n", incident.Service.Summary)
    89  				str += fmt.Sprintf(" Escalation: %s\n", incident.EscalationPolicy.Summary)
    90  				str += fmt.Sprintf("       Link: %s\n", incident.HTMLURL)
    91  			}
    92  		} else {
    93  			str += "\n No open incidents\n"
    94  		}
    95  
    96  		str += "\n"
    97  	}
    98  
    99  	onCallTree := make(map[string][]pagerduty.OnCall)
   100  
   101  	filter := make(map[string]bool)
   102  	for _, item := range widget.settings.escalationFilter {
   103  		filter[item.(string)] = true
   104  	}
   105  
   106  	// OnCalls
   107  
   108  	for _, onCall := range onCalls {
   109  		summary := onCall.EscalationPolicy.Summary
   110  		if len(widget.settings.escalationFilter) == 0 || filter[summary] {
   111  			onCallTree[summary] = append(onCallTree[summary], onCall)
   112  		}
   113  	}
   114  
   115  	// We want to sort our escalation policies for predictability/ease of finding
   116  	keys := make([]string, 0, len(onCallTree))
   117  	for k := range onCallTree {
   118  		keys = append(keys, k)
   119  	}
   120  
   121  	sort.Strings(keys)
   122  
   123  	if len(keys) > 0 {
   124  		str += fmt.Sprintf("[%s] Schedules[white]\n", widget.settings.Colors.Subheading)
   125  
   126  		// Print out policies, and escalation order of users
   127  		for _, key := range keys {
   128  			str += fmt.Sprintf(
   129  				"\n [%s]%s\n",
   130  				widget.settings.Colors.Label,
   131  				key,
   132  			)
   133  
   134  			values := onCallTree[key]
   135  			sort.Sort(ByEscalationLevel(values))
   136  
   137  			for _, onCall := range values {
   138  				str += fmt.Sprintf(
   139  					" [%s]%d - %s\n",
   140  					widget.settings.Colors.Text,
   141  					onCall.EscalationLevel,
   142  					widget.userSummary(&onCall),
   143  				)
   144  
   145  				onCallEnd := widget.onCallEndSummary(&onCall)
   146  				if onCallEnd != "" {
   147  					str += fmt.Sprintf(
   148  						"     %s\n",
   149  						onCallEnd,
   150  					)
   151  				}
   152  			}
   153  		}
   154  	}
   155  
   156  	return str
   157  }
   158  
   159  // onCallEndSummary may or may not return the date that the specified onCall schedule ends
   160  func (widget *Widget) onCallEndSummary(onCall *pagerduty.OnCall) string {
   161  	if !widget.settings.showOnCallEnd {
   162  		return ""
   163  	}
   164  
   165  	if onCall.End == "" {
   166  		return ""
   167  	}
   168  
   169  	end, err := time.Parse(onCallTimeAPILayout, onCall.End)
   170  	if err != nil {
   171  		return ""
   172  	}
   173  
   174  	return end.Format(onCallTimeDisplayLayout)
   175  }
   176  
   177  // userSummary returns the name of the person assigned to the specified onCall schedule
   178  func (widget *Widget) userSummary(onCall *pagerduty.OnCall) string {
   179  	summary := onCall.User.Summary
   180  
   181  	if summary == widget.settings.myName {
   182  		summary = fmt.Sprintf("[::b]%s", summary)
   183  	}
   184  
   185  	return summary
   186  }