Skip to content

Commit

Permalink
Merge pull request #191 from garethjevans/issues
Browse files Browse the repository at this point in the history
  • Loading branch information
garethjevans authored Jan 15, 2022
2 parents 7013f2b + 8ce9365 commit 46adf72
Show file tree
Hide file tree
Showing 6 changed files with 623 additions and 8 deletions.
1 change: 1 addition & 0 deletions pkg/cmd/getcmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func NewGetCmd() *cobra.Command {

cmd.AddCommand(NewGetPrsCmd())
cmd.AddCommand(NewGetReposCmd())
cmd.AddCommand(NewGetIssuesCmd())

return cmd
}
Expand Down
153 changes: 153 additions & 0 deletions pkg/cmd/getcmd/get_issues.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package getcmd

import (
"fmt"
"strings"

"github.com/plumming/dx/pkg/cmd"
"github.com/plumming/dx/pkg/domain"

"github.com/pkg/errors"

"github.com/plumming/dx/pkg/util"

"github.com/jenkins-x/jx-logging/pkg/log"
"github.com/plumming/dx/pkg/table"
"github.com/spf13/cobra"
)

type GetIssuesCmd struct {
cmd.CommonCmd
ShowBots bool
ShowHidden bool
Review bool
Quiet bool
Me bool
Raw string
Cmd *cobra.Command
Args []string
}

func NewGetIssuesCmd() *cobra.Command {
c := &GetIssuesCmd{}
cmd := &cobra.Command{
Use: "issues",
Short: "Gets your open issues",
Long: "",
Example: `Get a list of open issues:
dx get issues
Get a list of your issues:
dx get issues --me
Get a list of issues requiring review:
dx get issues --review
Get a list of issues with a custom query:
dx get issues --raw is:private
`,
Aliases: []string{"issues", "is"},
Run: func(cmd *cobra.Command, args []string) {
c.Cmd = cmd
c.Args = args
err := c.Run()
if err != nil {
log.Logger().Fatalf("unable to run command: %s", err)
}
},
Args: cobra.NoArgs,
}

c.AddOptions(cmd)

cmd.Flags().BoolVarP(&c.ShowBots, "show-bots", "", false,
"Show bot account PRs (default: false)")
cmd.Flags().BoolVarP(&c.ShowHidden, "show-hidden", "", false,
"Show PRs that are filtered by hidden labels (default: false)")
cmd.Flags().BoolVarP(&c.Review, "review", "", false,
"Show PRs that are ready for review")
cmd.Flags().BoolVarP(&c.Quiet, "quiet", "", false,
"Hide the column headings")
cmd.Flags().BoolVarP(&c.Me, "me", "m", false,
"Show all PRs that are created by the author")

cmd.Flags().StringVarP(&c.Raw, "raw", "", "",
"Additional raw search parameters to use when querying")

return cmd
}

func (c *GetIssuesCmd) Run() error {
d := domain.NewGetIssues()
d.ShowHidden = c.ShowHidden
d.ShowBots = c.ShowBots
d.Me = c.Me
d.Review = c.Review
d.Raw = c.Raw

err := d.Validate()
if err != nil {
return errors.Wrap(err, "validate failed")
}

err = d.Run()
if err != nil {
return errors.Wrap(err, "run failed")
}

if c.Query != "" {
fmt.Println(c.Filter(d.Issues))
return nil
}

table := table.NewTable(c.Cmd.OutOrStdout())

pullURL := ""
if !c.Quiet {
table.AddRow(
"Issue#",
"Author",
"Title",
"Age",
"Labels",
)
}

for _, pr := range d.Issues {
if pullURL != pr.IssueString() {
table.AddRow(fmt.Sprintf("# %s", util.ColorAnswer(pr.IssueString())))
pullURL = pr.IssueString()
}
table.AddRow(
util.ColorInfo(fmt.Sprintf("#%d", pr.Number)),
pr.Author.Login,
pr.ColoredTitle(),
util.SafeTime(&pr.CreatedAt),
pr.LabelsString(),
)
}

table.Render()

if len(d.Issues) > 0 {
fmt.Printf("\nDisplaying %d Issue(s)\n", len(d.Issues))
}

if (d.FilteredBotAccounts + d.FilteredLabels) > 0 {
flags := []string{}
if d.FilteredBotAccounts > 0 {
flags = append(flags, "--show-bots")
}
if d.FilteredLabels > 0 {
flags = append(flags, "--show-hidden")
}
fmt.Printf("\nFiltered %d Issues, use %s to view them\n", (d.FilteredBotAccounts + d.FilteredLabels), strings.Join(flags, ", "))
}

return nil
}
186 changes: 186 additions & 0 deletions pkg/domain/get_issues.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package domain

import (
"fmt"
"sort"
"strings"
"time"

"github.com/plumming/dx/pkg/config"

"github.com/plumming/dx/pkg/pr"

"github.com/jenkins-x/jx-logging/pkg/log"
"github.com/plumming/dx/pkg/cmd"
)

var (
getIssuesQuery = `{
search(query: "is:issue is:open %s", type: ISSUE, first: %d) {
nodes {
... on Issue {
number
title
url
createdAt
closed
author {
login
}
repository {
nameWithOwner
}
labels(first: 10) {
nodes {
name
}
}
}
}
}
}`
)

// GetIssues defines get pull issues response.
type GetIssues struct {
cmd.CommonOptions
ShowBots bool
ShowHidden bool
Me bool
Review bool
Raw string
Issues []pr.Issue
FilteredLabels int
FilteredBotAccounts int
}

// IssueData.
type IssueData struct {
Search IssueSearch `json:"search"`
}

// IssueSearch.
type IssueSearch struct {
Issues []pr.Issue `json:"nodes"`
}

// NewGetIssues.
func NewGetIssues() *GetIssues {
g := &GetIssues{}
return g
}

// Validate input.
func (g *GetIssues) Validate() error {
return nil
}

// Run the cmd.
func (g *GetIssues) Run() error {
cfg, err := g.DxConfig()
if err != nil {
return err
}

var issues []pr.Issue

for _, host := range cfg.GetConfiguredServers() {
i, err := g.GetIssuesForHost(host, cfg, getIssuesQuery)
if err != nil {
return err
}
issues = append(issues, i...)
}

sort.Sort(pr.ByIssuesString(issues))

var issuesToReturn []pr.Issue

filteredOnLabels := 0
filteredOnAccounts := 0

for _, issue := range issues {
if issue.Display() {
if g.filterOnLabels(issue, cfg.GetHiddenLabels()) {
filteredOnLabels++
} else if g.filterOnAccounts(issue, cfg.GetBotAccounts()) {
filteredOnAccounts++
} else {
issuesToReturn = append(issuesToReturn, issue)
}
}
}

log.Logger().Debugf("Filtered %d/%d Issue(s)", filteredOnLabels, filteredOnAccounts)

g.Issues = issuesToReturn
g.FilteredLabels = filteredOnLabels
g.FilteredBotAccounts = filteredOnAccounts

return nil
}

func (g *GetIssues) GetIssuesForHost(host string, cfg config.Config, query string) ([]pr.Issue, error) {
client, err := g.GithubClient()
if err != nil {
return nil, err
}
currentUser, err := GetCurrentUser(client, host)
if err != nil {
return nil, err
}

var queryString string
if g.Raw != "" {
queryString = g.Raw
} else if g.Me {
orgs, err := GetOrgsForUser(client, host)
if err != nil {
return nil, err
}
log.Logger().Debugf("User is a member of %s organisations", orgs)

userQuery := "user:" + strings.Join(orgs, " user:")
log.Logger().Debugf("User '%s'", userQuery)

queryString = fmt.Sprintf("author:%s %s", currentUser, userQuery)
} else if g.Review {
queryString = fmt.Sprintf("review-requested:%s", currentUser)
} else {
queryString = strings.Join(cfg.GetReposToQuery(host), " ")
}

if cfg.GetMaxAgeOfPRs() != -1 {
dateString := time.Now().AddDate(0, 0, -1*cfg.GetMaxAgeOfPRs()).Format("2006-01-02")
queryString = queryString + " created:>" + dateString
}

queryToRun := fmt.Sprintf(query, queryString, cfg.GetMaxNumberOfPRs())
log.Logger().Debugf("running query\n%s", queryToRun)

data := IssueData{}
err = client.GraphQL(host, queryToRun, nil, &data)
if err != nil {
return nil, err
}

return data.Search.Issues, nil
}

func (g *GetIssues) filterOnAccounts(pr pr.Issue, botAccounts []string) bool {
for _, botAccount := range botAccounts {
if pr.Author.Login == botAccount {
return !g.ShowBots
}
}
return false
}

func (g *GetIssues) filterOnLabels(pr pr.Issue, hiddenLabels []string) bool {
for _, label := range hiddenLabels {
if pr.HasLabel(label) {
return !g.ShowHidden
}
}
return false
}
Loading

0 comments on commit 46adf72

Please sign in to comment.