github.com/zaquestion/lab@v0.25.1/cmd/issue_list.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/MakeNowJust/heredoc/v2"
     8  	"github.com/pkg/errors"
     9  	"github.com/rsteube/carapace"
    10  	"github.com/spf13/cobra"
    11  	gitlab "github.com/xanzy/go-gitlab"
    12  	"github.com/zaquestion/lab/internal/action"
    13  	lab "github.com/zaquestion/lab/internal/gitlab"
    14  )
    15  
    16  var (
    17  	issueLabels     []string
    18  	issueMilestone  string
    19  	issueState      string
    20  	issueSearch     string
    21  	issueNumRet     string
    22  	issueAll        bool
    23  	issueExactMatch bool
    24  	issueAssignee   string
    25  	issueAssigneeID *int
    26  	issueAuthor     string
    27  	issueAuthorID   *int
    28  	issueOrder      string
    29  	issueSortedBy   string
    30  )
    31  
    32  var issueListCmd = &cobra.Command{
    33  	Use:     "list [remote] [search]",
    34  	Aliases: []string{"ls", "search"},
    35  	Short:   "List issues",
    36  	Example: heredoc.Doc(`
    37  		lab issue list
    38  		lab issue list "search terms"
    39  		lab issue list origin "search terms"
    40  		lab issue list origin --all
    41  		lab issue list origin --assignee johndoe
    42  		lab issue list upstream --author janedoe
    43  		lab issue list upstream -x "An Issue with Abc"
    44  		lab issue list upstream -l "new_bug"
    45  		lab issue list upstream --milestone "week 22"
    46  		lab issue list remote -n "10"
    47  		lab issue list remote --order "created_at"
    48  		lab issue list remote --sort "asc"
    49  		lab issue list remote --state "closed"`),
    50  	PersistentPreRun: labPersistentPreRun,
    51  	Run: func(cmd *cobra.Command, args []string) {
    52  		issues, err := issueList(args)
    53  		if err != nil {
    54  			log.Fatal(err)
    55  		}
    56  
    57  		pager := newPager(cmd.Flags())
    58  		defer pager.Close()
    59  
    60  		for _, issue := range issues {
    61  			fmt.Printf("#%d %s\n", issue.IID, issue.Title)
    62  		}
    63  	},
    64  }
    65  
    66  func issueList(args []string) ([]*gitlab.Issue, error) {
    67  	rn, search, err := parseArgsRemoteAndProject(args)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	issueSearch = search
    72  
    73  	labels, err := mapLabels(rn, issueLabels)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	if issueMilestone != "" {
    79  		milestone, err := lab.MilestoneGet(rn, issueMilestone)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  		issueMilestone = milestone.Title
    84  	}
    85  
    86  	num, err := strconv.Atoi(issueNumRet)
    87  	if issueAll || (err != nil) {
    88  		num = -1
    89  	}
    90  
    91  	// gitlab lib still doesn't have search by author username for issues,
    92  	// because of that we need to get user's ID for both assignee and
    93  	// author.
    94  	if issueAuthor != "" {
    95  		issueAuthorID = getUserID(issueAuthor)
    96  		if issueAuthorID == nil {
    97  			log.Fatalf("%s user not found\n", issueAuthor)
    98  		}
    99  	}
   100  
   101  	if issueAssignee != "" {
   102  		issueAssigneeID = getUserID(issueAssignee)
   103  		if issueAssigneeID == nil {
   104  			log.Fatalf("%s user not found\n", issueAssignee)
   105  		}
   106  	}
   107  
   108  	orderBy := gitlab.String(issueOrder)
   109  
   110  	sort := gitlab.String(issueSortedBy)
   111  
   112  	opts := gitlab.ListProjectIssuesOptions{
   113  		ListOptions: gitlab.ListOptions{
   114  			PerPage: num,
   115  		},
   116  		Labels:     labels,
   117  		Milestone:  &issueMilestone,
   118  		State:      &issueState,
   119  		OrderBy:    orderBy,
   120  		Sort:       sort,
   121  		AuthorID:   issueAuthorID,
   122  		AssigneeID: issueAssigneeID,
   123  	}
   124  
   125  	if issueExactMatch {
   126  		if issueSearch == "" {
   127  			return nil, errors.New("Exact match requested, but no search terms provided")
   128  		}
   129  		issueSearch = "\"" + issueSearch + "\""
   130  	}
   131  
   132  	if issueSearch != "" {
   133  		opts.Search = &issueSearch
   134  	}
   135  
   136  	return lab.IssueList(rn, opts, num)
   137  }
   138  
   139  func init() {
   140  	issueListCmd.Flags().StringSliceVarP(
   141  		&issueLabels, "label", "l", []string{},
   142  		"filter issues by label")
   143  	issueListCmd.Flags().StringVarP(
   144  		&issueState, "state", "s", "opened",
   145  		"filter issues by state (all/opened/closed)")
   146  	issueListCmd.Flags().StringVarP(
   147  		&issueNumRet, "number", "n", "10",
   148  		"number of issues to return")
   149  	issueListCmd.Flags().BoolVarP(
   150  		&issueAll, "all", "a", false,
   151  		"list all issues on the project")
   152  	issueListCmd.Flags().StringVar(
   153  		&issueMilestone, "milestone", "",
   154  		"filter issues by milestone")
   155  	issueListCmd.Flags().StringVar(
   156  		&issueAssignee, "assignee", "",
   157  		"filter issues by assignee")
   158  	issueListCmd.Flags().StringVar(
   159  		&issueAuthor, "author", "",
   160  		"filter issues by author")
   161  	issueListCmd.Flags().BoolVarP(
   162  		&issueExactMatch, "exact-match", "x", false,
   163  		"match on the exact (case-insensitive) search terms")
   164  	issueListCmd.Flags().StringVar(&issueOrder, "order", "updated_at", "display order (updated_at/created_at)")
   165  	issueListCmd.Flags().StringVar(&issueSortedBy, "sort", "desc", "sort order (desc/asc)")
   166  
   167  	issueCmd.AddCommand(issueListCmd)
   168  	carapace.Gen(issueListCmd).FlagCompletion(carapace.ActionMap{
   169  		"label": carapace.ActionMultiParts(",", func(c carapace.Context) carapace.Action {
   170  			project, _, err := parseArgsRemoteAndProject(c.Args)
   171  			if err != nil {
   172  				return carapace.ActionMessage(err.Error())
   173  			}
   174  			return action.Labels(project).Invoke(c).Filter(c.Parts).ToA()
   175  		}),
   176  		"milestone": carapace.ActionCallback(func(c carapace.Context) carapace.Action {
   177  			project, _, err := parseArgsRemoteAndProject(c.Args)
   178  			if err != nil {
   179  				return carapace.ActionMessage(err.Error())
   180  			}
   181  			return action.Milestones(project, action.MilestoneOpts{Active: true})
   182  		}),
   183  		"state": carapace.ActionValues("all", "opened", "closed"),
   184  	})
   185  	carapace.Gen(issueListCmd).PositionalCompletion(
   186  		action.Remotes(),
   187  	)
   188  }