add search feature in listing issues
This commit is contained in:
parent
2ab073d599
commit
f791bbdbcb
6 changed files with 300 additions and 54 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
pkg/cmd/issue/list/fixtures/issueSearch.json
Normal file
60
pkg/cmd/issue/list/fixtures/issueSearch.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue