1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

Migrate all modules to their own subfolder

Handles #375
This commit is contained in:
Sean Smith
2019-02-18 11:16:34 -05:00
parent c28c31aedb
commit 8030380f89
123 changed files with 51 additions and 51 deletions

123
modules/jira/client.go Normal file
View File

@@ -0,0 +1,123 @@
package jira
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/wtfutil/wtf/wtf"
)
func IssuesFor(username string, projects []string, jql string) (*SearchResult, error) {
query := []string{}
var projQuery = getProjectQuery(projects)
if projQuery != "" {
query = append(query, projQuery)
}
if username != "" {
query = append(query, buildJql("assignee", username))
}
if jql != "" {
query = append(query, jql)
}
v := url.Values{}
v.Set("jql", strings.Join(query, " AND "))
url := fmt.Sprintf("/rest/api/2/search?%s", v.Encode())
resp, err := jiraRequest(url)
if err != nil {
return &SearchResult{}, err
}
searchResult := &SearchResult{}
parseJson(searchResult, resp.Body)
return searchResult, nil
}
func buildJql(key string, value string) string {
return fmt.Sprintf("%s = \"%s\"", key, value)
}
/* -------------------- Unexported Functions -------------------- */
func apiKey() string {
return wtf.Config.UString(
"wtf.mods.jira.apiKey",
os.Getenv("WTF_JIRA_API_KEY"),
)
}
func jiraRequest(path string) (*http.Response, error) {
url := fmt.Sprintf("%s%s", wtf.Config.UString("wtf.mods.jira.domain"), path)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(wtf.Config.UString("wtf.mods.jira.email"), apiKey())
verifyServerCertificate := wtf.Config.UBool("wtf.mods.jira.verifyServerCertificate", true)
httpClient := &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: !verifyServerCertificate,
},
Proxy: http.ProxyFromEnvironment,
},
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf(resp.Status)
}
return resp, nil
}
func parseJson(obj interface{}, text io.Reader) {
jsonStream, err := ioutil.ReadAll(text)
if err != nil {
panic(err)
}
decoder := json.NewDecoder(bytes.NewReader(jsonStream))
for {
if err := decoder.Decode(obj); err == io.EOF {
break
} else if err != nil {
panic(err)
}
}
}
func getProjectQuery(projects []string) string {
singleEmptyProject := len(projects) == 1 && len(projects[0]) == 0
if len(projects) == 0 || singleEmptyProject {
return ""
} else if len(projects) == 1 {
return buildJql("project", projects[0])
}
quoted := make([]string, len(projects))
for i := range projects {
quoted[i] = fmt.Sprintf("\"%s\"", projects[i])
}
return fmt.Sprintf("project in (%s)", strings.Join(quoted, ", "))
}

32
modules/jira/issues.go Normal file
View File

@@ -0,0 +1,32 @@
package jira
type Issue struct {
Expand string `json:"expand"`
ID string `json:"id"`
Self string `json:"self"`
Key string `json:"key"`
IssueFields *IssueFields `json:"fields"`
}
type IssueFields struct {
Summary string `json:"summary"`
IssueType *IssueType `json:"issuetype"`
IssueStatus *IssueStatus `json:"status"`
}
type IssueType struct {
Self string `json:"self"`
ID string `json:"id"`
Description string `json:"description"`
IconURL string `json:"iconUrl"`
Name string `json:"name"`
Subtask bool `json:"subtask"`
}
type IssueStatus struct {
ISelf string `json:"self"`
IDescription string `json:"description"`
IName string `json:"name"`
}

View File

@@ -0,0 +1,8 @@
package jira
type SearchResult struct {
StartAt int `json:"startAt"`
MaxResults int `json:"maxResults"`
Total int `json:"total"`
Issues []Issue `json:"issues"`
}

213
modules/jira/widget.go Normal file
View File

@@ -0,0 +1,213 @@
package jira
import (
"fmt"
"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/wtf"
"strconv"
)
const HelpText = `
Keyboard commands for Jira:
/: Show/hide this help window
j: Select the next item in the list
k: Select the previous item in the list
arrow down: Select the next item in the list
arrow up: Select the previous item in the list
return: Open the selected issue in a browser
`
type Widget struct {
wtf.HelpfulWidget
wtf.TextWidget
result *SearchResult
selected int
}
func NewWidget(app *tview.Application, pages *tview.Pages) *Widget {
widget := Widget{
HelpfulWidget: wtf.NewHelpfulWidget(app, pages, HelpText),
TextWidget: wtf.NewTextWidget(app, "Jira", "jira", true),
}
widget.HelpfulWidget.SetView(widget.View)
widget.unselect()
widget.View.SetScrollable(true)
widget.View.SetRegions(true)
widget.View.SetInputCapture(widget.keyboardIntercept)
return &widget
}
/* -------------------- Exported Functions -------------------- */
func (widget *Widget) Refresh() {
searchResult, err := IssuesFor(
wtf.Config.UString("wtf.mods.jira.username"),
getProjects(),
wtf.Config.UString("wtf.mods.jira.jql", ""),
)
if err != nil {
widget.result = nil
widget.View.SetWrap(true)
widget.View.SetTitle(widget.Name)
widget.View.SetText(err.Error())
} else {
widget.result = searchResult
}
widget.display()
}
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) display() {
if widget.result == nil {
return
}
widget.View.SetWrap(false)
str := fmt.Sprintf("%s- [green]%s[white]", widget.Name, wtf.Config.UString("wtf.mods.jira.project"))
widget.View.Clear()
widget.View.SetTitle(widget.ContextualTitle(str))
widget.View.SetText(fmt.Sprintf("%s", widget.contentFrom(widget.result)))
widget.View.Highlight(strconv.Itoa(widget.selected)).ScrollToHighlight()
}
func (widget *Widget) next() {
widget.selected++
if widget.result != nil && widget.selected >= len(widget.result.Issues) {
widget.selected = 0
}
}
func (widget *Widget) prev() {
widget.selected--
if widget.selected < 0 && widget.result != nil {
widget.selected = len(widget.result.Issues) - 1
}
}
func (widget *Widget) openItem() {
sel := widget.selected
if sel >= 0 && widget.result != nil && sel < len(widget.result.Issues) {
issue := &widget.result.Issues[widget.selected]
wtf.OpenFile(wtf.Config.UString("wtf.mods.jira.domain") + "/browse/" + issue.Key)
}
}
func (widget *Widget) unselect() {
widget.selected = -1
}
func (widget *Widget) contentFrom(searchResult *SearchResult) string {
str := " [red]Assigned Issues[white]\n"
for idx, issue := range searchResult.Issues {
fmtStr := fmt.Sprintf(
`["%d"][""][%s] [%s]%-6s[white] [green]%-10s[white] [yellow][%s][white] [%s]%s`,
idx,
widget.rowColor(idx),
widget.issueTypeColor(&issue),
issue.IssueFields.IssueType.Name,
issue.Key,
issue.IssueFields.IssueStatus.IName,
widget.rowColor(idx),
issue.IssueFields.Summary,
)
_, _, w, _ := widget.View.GetInnerRect()
fmtStr = fmtStr + wtf.PadRow(len(issue.IssueFields.Summary), w+1)
str = str + fmtStr + "\n"
}
return str
}
func (widget *Widget) rowColor(idx int) string {
if widget.View.HasFocus() && (idx == widget.selected) {
return wtf.DefaultFocussedRowColor()
}
return wtf.RowColor("jira", idx)
}
func (widget *Widget) issueTypeColor(issue *Issue) string {
switch issue.IssueFields.IssueType.Name {
case "Bug":
return "red"
case "Story":
return "blue"
case "Task":
return "orange"
default:
return "white"
}
}
func getProjects() []string {
// see if project is set to a single string
configPath := "wtf.mods.jira.project"
singleProject, err := wtf.Config.String(configPath)
if err == nil {
return []string{singleProject}
}
// else, assume list
projList := wtf.Config.UList(configPath)
var ret []string
for _, proj := range projList {
if str, ok := proj.(string); ok {
ret = append(ret, str)
}
}
return ret
}
func (widget *Widget) keyboardIntercept(event *tcell.EventKey) *tcell.EventKey {
switch string(event.Rune()) {
case "/":
widget.ShowHelp()
case "j":
// Select the next item down
widget.next()
widget.display()
return nil
case "k":
// Select the next item up
widget.prev()
widget.display()
return nil
}
switch event.Key() {
case tcell.KeyDown:
// Select the next item down
widget.next()
widget.display()
return nil
case tcell.KeyEnter:
widget.openItem()
return nil
case tcell.KeyEsc:
// Unselect the current row
widget.unselect()
widget.display()
return event
case tcell.KeyUp:
// Select the next item up
widget.prev()
widget.display()
return nil
default:
// Pass it along
return event
}
}