add search feature in listing issues

This commit is contained in:
Gowtham Munukutla 2021-03-10 16:14:32 +05:30 committed by Mislav Marohnić
parent 2ab073d599
commit f791bbdbcb
6 changed files with 300 additions and 54 deletions

View file

@ -6,49 +6,49 @@
"totalCount": 3,
"nodes": [
{
"number": 1,
"title": "number won",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
"number": 1,
"title": "number won",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
},
{
"number": 2,
"title": "number too",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
"number": 2,
"title": "number too",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
},
{
"number": 4,
"title": "number fore",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
"number": 4,
"title": "number fore",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
}
]
}
}
}
}
}

View file

@ -0,0 +1,60 @@
{
"data": {
"repository": {
"hasIssuesEnabled": true
},
"search": {
"issueCount": 3,
"edges": [
{
"node": {
"number": 1,
"title": "number won",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
}
},
{
"node": {
"number": 2,
"title": "number too",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
}
},
{
"node": {
"number": 4,
"title": "number fore",
"url": "https://wow.com",
"updatedAt": "2011-01-26T19:01:12Z",
"labels": {
"nodes": [
{
"name": "label"
}
],
"totalCount": 1
}
}
}
]
}
}
}

View file

@ -32,6 +32,7 @@ type ListOptions struct {
Author string
Mention string
Milestone string
Search string
}
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
@ -50,6 +51,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
$ gh issue list -a @me
$ gh issue list --web
$ gh issue list --milestone 'MVP'
$ gh issue list --search "error no:assignee sort:created-asc"
`),
Args: cmdutil.NoArgsQuoteReminder,
RunE: func(cmd *cobra.Command, args []string) error {
@ -75,7 +77,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
cmd.Flags().StringVarP(&opts.Author, "author", "A", "", "Filter by author")
cmd.Flags().StringVar(&opts.Mention, "mention", "", "Filter by mention")
cmd.Flags().StringVarP(&opts.Milestone, "milestone", "m", "", "Filter by milestone `number` or `title`")
cmd.Flags().StringVarP(&opts.Search, "search", "S", "", "Search issues with filter")
return cmd
}
@ -107,29 +109,44 @@ func listRun(opts *ListOptions) error {
return err
}
filterOptions := prShared.FilterOptions{
Entity: "issue",
State: opts.State,
Assignee: filterAssignee,
Labels: opts.Labels,
Author: filterAuthor,
Mention: filterMention,
Milestone: opts.Milestone,
Search: opts.Search,
}
if opts.WebMode {
issueListURL := ghrepo.GenerateRepoURL(baseRepo, "issues")
openURL, err := prShared.ListURLWithQuery(issueListURL, prShared.FilterOptions{
Entity: "issue",
State: opts.State,
Assignee: filterAssignee,
Labels: opts.Labels,
Author: filterAuthor,
Mention: filterMention,
Milestone: opts.Milestone,
})
openURL, err := prShared.ListURLWithQuery(issueListURL, filterOptions)
if err != nil {
return err
}
if isTerminal {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL))
}
return utils.OpenInBrowser(openURL)
}
listResult, err := api.IssueList(apiClient, baseRepo, opts.State, opts.Labels, filterAssignee, opts.LimitResults, filterAuthor, filterMention, opts.Milestone)
if err != nil {
return err
searchQuery := prShared.IssueSearchBuild(filterOptions)
var listResult *api.IssuesAndTotalCount
if opts.Search != "" {
listResult, err = api.IssueSearch(apiClient, baseRepo, searchQuery, opts.LimitResults)
if err != nil {
return err
}
} else {
listResult, err = api.IssueList(apiClient, baseRepo, opts.State, opts.Labels, filterAssignee, opts.LimitResults, filterAuthor, filterMention, opts.Milestone)
if err != nil {
return err
}
}
err = opts.IO.StartPager()

View file

@ -58,6 +58,7 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err
ErrBuf: stderr,
}, err
}
func TestIssueList_nontty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
@ -296,3 +297,52 @@ func TestIssueList_milestoneByNumber(t *testing.T) {
t.Fatalf("error running issue list: %v", err)
}
}
func TestIssueList_Search_tty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.FileResponse("./fixtures/issueSearch.json"))
output, err := runCommand(http, true, "--search \"auth bug\"")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
out := output.String()
timeRE := regexp.MustCompile(`\d+ years`)
out = timeRE.ReplaceAllString(out, "X years")
assert.Equal(t, heredoc.Doc(`
Showing 3 of 3 open issues in OWNER/REPO
#1 number won (label) about X years ago
#2 number too (label) about X years ago
#4 number fore (label) about X years ago
`), out)
}
func TestIssueList_Search_web(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`https://github\.com`, 0, "", func(args []string) {
url := strings.ReplaceAll(args[len(args)-1], "^", "")
assert.Equal(t, "https://github.com/OWNER/REPO/issues?q=is%3Aissue+assignee%3Apeter+label%3Abug+label%3Adocs+author%3Ajohn+mentions%3Afrank+milestone%3Av1.1+transfer", url)
})
output, err := runCommand(http, true, "--web -a peter -A john -l bug -l docs -L 10 -s all --mention frank --milestone v1.1 --search transfer")
if err != nil {
t.Errorf("error running command `issue list` with `--web` flag: %v", err)
}
assert.Equal(t, "", output.String())
assert.Equal(t, "Opening github.com/OWNER/REPO/issues in your browser.\n", output.Stderr())
}

View file

@ -149,6 +149,7 @@ type FilterOptions struct {
BaseBranch string
Mention string
Milestone string
Search string
}
func ListURLWithQuery(listURL string, options FilterOptions) (string, error) {
@ -156,6 +157,16 @@ func ListURLWithQuery(listURL string, options FilterOptions) (string, error) {
if err != nil {
return "", err
}
query := IssueSearchBuild(options)
q := u.Query()
q.Set("q", strings.TrimSuffix(query, " "))
u.RawQuery = q.Encode()
return u.String(), nil
}
func IssueSearchBuild(options FilterOptions) string {
query := fmt.Sprintf("is:%s ", options.Entity)
if options.State != "all" {
query += fmt.Sprintf("is:%s ", options.State)
@ -178,10 +189,11 @@ func ListURLWithQuery(listURL string, options FilterOptions) (string, error) {
if options.Milestone != "" {
query += fmt.Sprintf("milestone:%s ", quoteValueForQuery(options.Milestone))
}
q := u.Query()
q.Set("q", strings.TrimSuffix(query, " "))
u.RawQuery = q.Encode()
return u.String(), nil
if options.Search != "" {
query += options.Search
}
return query
}
func quoteValueForQuery(v string) string {