From b90870bfb04f6fd823839774e0c27ae2f5c4dc50 Mon Sep 17 00:00:00 2001 From: Kevin Minehart Date: Mon, 14 Sep 2020 19:54:37 -0500 Subject: [PATCH 1/2] use field when searching pull requests if a repository is not provided --- pkg/github/pull_requests.go | 39 ++++++++++++++++++++------------ pkg/github/pull_requests_test.go | 33 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/pkg/github/pull_requests.go b/pkg/github/pull_requests.go index 443d6827..c472539d 100644 --- a/pkg/github/pull_requests.go +++ b/pkg/github/pull_requests.go @@ -125,19 +125,10 @@ func (p PullRequests) Frames() data.Frames { // GetAllPullRequests uses the graphql search endpoint API to search all pull requests in the repository func GetAllPullRequests(ctx context.Context, client Client, opts models.ListPullRequestsOptions) (PullRequests, error) { - search := []string{ - "is:pr", - fmt.Sprintf("repo:%s/%s", opts.Owner, opts.Repository), - } - - if opts.Query != nil { - search = append(search, *opts.Query) - } - var ( variables = map[string]interface{}{ "cursor": (*githubv4.String)(nil), - "query": githubv4.String(strings.Join(search, " ")), + "query": githubv4.String(buildQuery(opts)), } pullRequests = []PullRequest{} @@ -167,15 +158,16 @@ func GetAllPullRequests(ctx context.Context, client Client, opts models.ListPull // GetPullRequestsInRange uses the graphql search endpoint API to find pull requests in the given time range. func GetPullRequestsInRange(ctx context.Context, client Client, opts models.ListPullRequestsOptions, from time.Time, to time.Time) (PullRequests, error) { - search := []string{} + var q string + if opts.TimeField != models.PullRequestNone { - search = []string{fmt.Sprintf("%s:%s..%s", opts.TimeField.String(), from.Format(time.RFC3339), to.Format(time.RFC3339))} + q = fmt.Sprintf("%s:%s..%s", opts.TimeField.String(), from.Format(time.RFC3339), to.Format(time.RFC3339)) } + if opts.Query != nil { - search = append(search, *opts.Query) + q = fmt.Sprintf("%s %s", *opts.Query, q) } - q := strings.Join(search, " ") return GetAllPullRequests(ctx, client, models.ListPullRequestsOptions{ Repository: opts.Repository, Owner: opts.Owner, @@ -183,3 +175,22 @@ func GetPullRequestsInRange(ctx context.Context, client Client, opts models.List Query: &q, }) } + +// buildQuery builds the "query" field for Pull Request searches +func buildQuery(opts models.ListPullRequestsOptions) string { + search := []string{ + "is:pr", + } + + if opts.Repository == "" { + search = append(search, fmt.Sprintf("org:%s", opts.Owner)) + } else { + search = append(search, fmt.Sprintf("repo:%s/%s", opts.Owner, opts.Repository)) + } + + if opts.Query != nil { + search = append(search, *opts.Query) + } + + return strings.Join(search, " ") +} diff --git a/pkg/github/pull_requests_test.go b/pkg/github/pull_requests_test.go index 5b190614..af0c30ee 100644 --- a/pkg/github/pull_requests_test.go +++ b/pkg/github/pull_requests_test.go @@ -113,3 +113,36 @@ func TestPullRequestsDataFrame(t *testing.T) { t.Fatal(err) } } + +func TestBuildQuery(t *testing.T) { + t.Run("Searching pull requests with a Repository and organization should use the repo field", func(t *testing.T) { + opts := models.ListPullRequestsOptions{ + Owner: "grafana", + Repository: "github-datasource", + } + + var ( + result = buildQuery(opts) + expect = "is:pr repo:grafana/github-datasource" + ) + + if result != expect { + t.Fatalf("Unexpected result from buildQuery. Expected '%s', received '%s'", expect, result) + } + }) + + t.Run("Issue #61 - Searching pull requests without a Repository should search the entire org", func(t *testing.T) { + opts := models.ListPullRequestsOptions{ + Owner: "test", + Repository: "", + } + + var ( + result = buildQuery(opts) + expect = "is:pr org:test" + ) + if result != expect { + t.Fatalf("Unexpected result from buildQuery. Expected '%s', received '%s'", expect, result) + } + }) +} From c712192a36c9170a1a062330509431d4663c57cc Mon Sep 17 00:00:00 2001 From: Kevin Minehart Date: Mon, 14 Sep 2020 20:03:12 -0500 Subject: [PATCH 2/2] Remove limit from Repositories, add extra fields to pull request frames --- pkg/github/pull_requests.go | 35 ++++++++++++-------- pkg/github/pull_requests_test.go | 18 +++++++--- pkg/github/repositories.go | 2 +- pkg/github/repository.go | 1 - pkg/github/testdata/pull_requests.golden.txt | 20 +++++------ 5 files changed, 47 insertions(+), 29 deletions(-) delete mode 100644 pkg/github/repository.go diff --git a/pkg/github/pull_requests.go b/pkg/github/pull_requests.go index c472539d..1c39cc3f 100644 --- a/pkg/github/pull_requests.go +++ b/pkg/github/pull_requests.go @@ -38,19 +38,22 @@ type PullRequestAuthor struct { // PullRequest is a GitHub pull request type PullRequest struct { - Title string - State githubv4.PullRequestState - Author PullRequestAuthor - Closed bool - IsDraft bool - Locked bool - Merged bool - ClosedAt githubv4.DateTime - CreatedAt githubv4.DateTime - UpdatedAt githubv4.DateTime - MergedAt githubv4.DateTime - Mergeable githubv4.MergeableState - MergedBy *PullRequestAuthor + Number int64 + Title string + URL string + State githubv4.PullRequestState + Author PullRequestAuthor + Closed bool + IsDraft bool + Locked bool + Merged bool + ClosedAt githubv4.DateTime + CreatedAt githubv4.DateTime + UpdatedAt githubv4.DateTime + MergedAt githubv4.DateTime + Mergeable githubv4.MergeableState + MergedBy *PullRequestAuthor + Repository Repository } // PullRequests is a list of GitHub Pull Requests @@ -65,7 +68,10 @@ func (p PullRequests) Frames() data.Frames { frame := data.NewFrame( "pull_requests", + data.NewField("number", nil, []int64{}), data.NewField("title", nil, []string{}), + data.NewField("url", nil, []string{}), + data.NewField("repository", nil, []string{}), data.NewField("state", nil, []string{}), data.NewField("author_login", nil, []string{}), data.NewField("author_email", nil, []string{}), @@ -102,7 +108,10 @@ func (p PullRequests) Frames() data.Frames { } frame.AppendRow( + v.Number, v.Title, + v.URL, + v.Repository.NameWithOwner, string(v.State), v.Author.User.Login, v.Author.User.Email, diff --git a/pkg/github/pull_requests_test.go b/pkg/github/pull_requests_test.go index af0c30ee..4d4768f8 100644 --- a/pkg/github/pull_requests_test.go +++ b/pkg/github/pull_requests_test.go @@ -56,11 +56,16 @@ func TestPullRequestsDataFrame(t *testing.T) { pullRequests := PullRequests{ PullRequest{ - Title: "PullRequest #1", - State: githubv4.PullRequestStateOpen, + Number: 1, + Title: "PullRequest #1", + URL: "https://github.com/grafana/github-datasource/pulls/1", + State: githubv4.PullRequestStateOpen, Author: PullRequestAuthor{ User: firstUser, }, + Repository: Repository{ + NameWithOwner: "grafana/github-datasource", + }, Closed: true, IsDraft: false, Locked: false, @@ -81,11 +86,16 @@ func TestPullRequestsDataFrame(t *testing.T) { MergedBy: nil, }, PullRequest{ - Title: "PullRequest #2", - State: githubv4.PullRequestStateOpen, + Number: 2, + Title: "PullRequest #2", + URL: "https://github.com/grafana/github-datasource/pulls/2", + State: githubv4.PullRequestStateOpen, Author: PullRequestAuthor{ User: secondUser, }, + Repository: Repository{ + NameWithOwner: "grafana/github-datasource", + }, Closed: true, IsDraft: false, Locked: false, diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index 0b8abbc4..4c1cd71d 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -97,7 +97,7 @@ func GetAllRepositories(ctx context.Context, client Client, opts models.ListRepo repos = []Repository{} ) - for i := 0; i < PageNumberLimit; i++ { + for { q := &QueryListRepositories{} if err := client.Query(ctx, q, variables); err != nil { return nil, err diff --git a/pkg/github/repository.go b/pkg/github/repository.go deleted file mode 100644 index d2e73c26..00000000 --- a/pkg/github/repository.go +++ /dev/null @@ -1 +0,0 @@ -package github diff --git a/pkg/github/testdata/pull_requests.golden.txt b/pkg/github/testdata/pull_requests.golden.txt index 38f5950e..cacbccac 100644 --- a/pkg/github/testdata/pull_requests.golden.txt +++ b/pkg/github/testdata/pull_requests.golden.txt @@ -2,16 +2,16 @@ Frame[0] Name: pull_requests -Dimensions: 15 Fields by 2 Rows -+----------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ -| Name: title | Name: state | Name: author_login | Name: author_email | Name: author_company | Name: closed | Name: is_draft | Name: locked | Name: merged | Name: mergeable | Name: closed_at | Name: merged_at | Name: updated_at | Name: created_at | Name: open_time | -| Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | -| Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []bool | Type: []bool | Type: []bool | Type: []bool | Type: []string | Type: []*time.Time | Type: []*time.Time | Type: []time.Time | Type: []time.Time | Type: []float64 | -+----------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ -| PullRequest #1 | OPEN | testUser | user@example.com | ACME corp | true | false | false | true | MERGEABLE | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | -6000 | -| PullRequest #2 | OPEN | testUser2 | user2@example.com | ACME corp | true | false | false | true | MERGEABLE | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 18:21:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | -6000 | -+----------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ +Dimensions: 18 Fields by 2 Rows ++---------------+----------------+------------------------------------------------------+---------------------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ +| Name: number | Name: title | Name: url | Name: repository | Name: state | Name: author_login | Name: author_email | Name: author_company | Name: closed | Name: is_draft | Name: locked | Name: merged | Name: mergeable | Name: closed_at | Name: merged_at | Name: updated_at | Name: created_at | Name: open_time | +| Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | Labels: | +| Type: []int64 | Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []string | Type: []bool | Type: []bool | Type: []bool | Type: []bool | Type: []string | Type: []*time.Time | Type: []*time.Time | Type: []time.Time | Type: []time.Time | Type: []float64 | ++---------------+----------------+------------------------------------------------------+---------------------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ +| 1 | PullRequest #1 | https://github.com/grafana/github-datasource/pulls/1 | grafana/github-datasource | OPEN | testUser | user@example.com | ACME corp | true | false | false | true | MERGEABLE | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | -6000 | +| 2 | PullRequest #2 | https://github.com/grafana/github-datasource/pulls/2 | grafana/github-datasource | OPEN | testUser2 | user2@example.com | ACME corp | true | false | false | true | MERGEABLE | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 14:41:56 +0000 +0000 | 2020-08-25 18:21:56 +0000 +0000 | 2020-08-25 16:21:56 +0000 +0000 | -6000 | ++---------------+----------------+------------------------------------------------------+---------------------------+----------------+--------------------+--------------------+----------------------+--------------+----------------+--------------+--------------+-----------------+---------------------------------+---------------------------------+---------------------------------+---------------------------------+-----------------+ ====== TEST DATA RESPONSE (arrow base64) ====== -FRAME=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aAEAAAAAAAAUAAAAAAAAAwMACgAYAAwACAAEAAoAAAAUAAAAWAIAAAIAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAQAAAAAAAAAEAAAAAAAAAACAAAAAAAAABIAAAAAAAAAAAAAAAAAAAASAAAAAAAAAAQAAAAAAAAAFgAAAAAAAAAGAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAAQAAAAAAAAAIAAAAAAAAAAKAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAqAAAAAAAAAAQAAAAAAAAALgAAAAAAAAAGAAAAAAAAADQAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAIAAAAAAAAANgAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAgAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAOAAAAAAAAAACAAAAAAAAADoAAAAAAAAAAAAAAAAAAAA6AAAAAAAAAAIAAAAAAAAAPAAAAAAAAAAAAAAAAAAAADwAAAAAAAAABAAAAAAAAAAAAEAAAAAAAAYAAAAAAAAABgBAAAAAAAAAAAAAAAAAAAYAQAAAAAAABAAAAAAAAAAKAEAAAAAAAAAAAAAAAAAACgBAAAAAAAAEAAAAAAAAAA4AQAAAAAAAAAAAAAAAAAAOAEAAAAAAAAQAAAAAAAAAEgBAAAAAAAAAAAAAAAAAABIAQAAAAAAABAAAAAAAAAAWAEAAAAAAAAAAAAAAAAAAFgBAAAAAAAAEAAAAAAAAAAAAAAADwAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAHAAAAAAAAABQdWxsUmVxdWVzdCAjMVB1bGxSZXF1ZXN0ICMyAAAAAAAAAAAEAAAACAAAAAAAAABPUEVOT1BFTgAAAAAIAAAAEQAAAAAAAAB0ZXN0VXNlcnRlc3RVc2VyMgAAAAAAAAAAAAAAEAAAACEAAAAAAAAAdXNlckBleGFtcGxlLmNvbXVzZXIyQGV4YW1wbGUuY29tAAAAAAAAAAAAAAAJAAAAEgAAAAAAAABBQ01FIGNvcnBBQ01FIGNvcnAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAJAAAAEgAAAAAAAABNRVJHRUFCTEVNRVJHRUFCTEUAAAAAAAAACA+34IkuFgAID7fgiS4WAAgPt+CJLhYACA+34IkuFgBo7bJVjy4WAKheFOKVLhYAaO2yVY8uFgBo7bJVjy4WAAAAAABwt8AAAAAAAHC3wBAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA4AAAAAAADAAEAAADoBgAAAAAAAJADAAAAAAAAaAEAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAXAAAAAIAAAAoAAAABAAAALD5//8IAAAADAAAAAAAAAAAAAAABQAAAHJlZklkAAAA0Pn//wgAAAAYAAAADQAAAHB1bGxfcmVxdWVzdHMAAAAEAAAAbmFtZQAAAAAPAAAA4AUAAHQFAAAIBQAAnAQAADAEAADUAwAAcAMAABQDAAC4AgAAVAIAAOQBAABsAQAABAEAAJwAAAAEAAAAbvr//xQAAABwAAAAcAAAAAAAAANwAAAAAgAAADAAAAAEAAAAYPr//wgAAAAUAAAACQAAAG9wZW5fdGltZQAAAAQAAABuYW1lAAAAAIj6//8IAAAAGAAAAAwAAAB7InVuaXQiOiJzIn0AAAAABgAAAGNvbmZpZwAAAAAAAF7+//8AAAIACQAAAG9wZW5fdGltZQAAAAL7//8UAAAAQAAAAEAAAAAAAAAKQAAAAAEAAAAEAAAA8Pr//wgAAAAUAAAACgAAAGNyZWF0ZWRfYXQAAAQAAABuYW1lAAAAAAAAAADC/v//AAADAAoAAABjcmVhdGVkX2F0AABm+///FAAAAEAAAABAAAAAAAAACkAAAAABAAAABAAAAFT7//8IAAAAFAAAAAoAAAB1cGRhdGVkX2F0AAAEAAAAbmFtZQAAAAAAAAAAJv///wAAAwAKAAAAdXBkYXRlZF9hdAAAnv///xQAAABAAAAAQAAAAAAACgFAAAAAAQAAAAQAAAC4+///CAAAABQAAAAJAAAAbWVyZ2VkX2F0AAAABAAAAG5hbWUAAAAAAAAAAIr///8AAAMACQAAAG1lcmdlZF9hdAASABgAFAATABIADAAAAAgABAASAAAAFAAAAEAAAABIAAAAAAAKAUgAAAABAAAABAAAACz8//8IAAAAFAAAAAkAAABjbG9zZWRfYXQAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAkAAABjbG9zZWRfYXQAAACq/P//FAAAAEAAAABAAAAAAAAABTwAAAABAAAABAAAAJj8//8IAAAAFAAAAAkAAABtZXJnZWFibGUAAAAEAAAAbmFtZQAAAAAAAAAAlPz//wkAAABtZXJnZWFibGUAAAAK/f//FAAAADwAAAA8AAAAAAAABjgAAAABAAAABAAAAPj8//8IAAAAEAAAAAYAAABtZXJnZWQAAAQAAABuYW1lAAAAAAAAAADw/P//BgAAAG1lcmdlZAAAYv3//xQAAAA8AAAAPAAAAAAAAAY4AAAAAQAAAAQAAABQ/f//CAAAABAAAAAGAAAAbG9ja2VkAAAEAAAAbmFtZQAAAAAAAAAASP3//wYAAABsb2NrZWQAALr9//8UAAAAQAAAAEAAAAAAAAAGPAAAAAEAAAAEAAAAqP3//wgAAAAUAAAACAAAAGlzX2RyYWZ0AAAAAAQAAABuYW1lAAAAAAAAAACk/f//CAAAAGlzX2RyYWZ0AAAAABr+//8UAAAAPAAAADwAAAAAAAAGOAAAAAEAAAAEAAAACP7//wgAAAAQAAAABgAAAGNsb3NlZAAABAAAAG5hbWUAAAAAAAAAAAD+//8GAAAAY2xvc2VkAABy/v//FAAAAEQAAABEAAAAAAAABUAAAAABAAAABAAAAGD+//8IAAAAGAAAAA4AAABhdXRob3JfY29tcGFueQAABAAAAG5hbWUAAAAAAAAAAGD+//8OAAAAYXV0aG9yX2NvbXBhbnkAANr+//8UAAAARAAAAEQAAAAAAAAFQAAAAAEAAAAEAAAAyP7//wgAAAAYAAAADAAAAGF1dGhvcl9lbWFpbAAAAAAEAAAAbmFtZQAAAAAAAAAAyP7//wwAAABhdXRob3JfZW1haWwAAAAAQv///xQAAABEAAAARAAAAAAAAAVAAAAAAQAAAAQAAAAw////CAAAABgAAAAMAAAAYXV0aG9yX2xvZ2luAAAAAAQAAABuYW1lAAAAAAAAAAAw////DAAAAGF1dGhvcl9sb2dpbgAAAACq////FAAAADwAAAA8AAAAAAAABTgAAAABAAAABAAAAJj///8IAAAAEAAAAAUAAABzdGF0ZQAAAAQAAABuYW1lAAAAAAAAAACQ////BQAAAHN0YXRlABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEgAAAAAAAAFRAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAFAAAAdGl0bGUAAAAEAAAAbmFtZQAAAAAAAAAABAAEAAQAAAAFAAAAdGl0bGUAAAAABwAAQVJST1cx +FRAME=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