From 730d49f788929cc6813b786883758ea5e3d31a17 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 17 Jan 2017 19:45:56 +0100 Subject: [PATCH 1/8] Add support for Commit Search endpoint Added support for this new preview endpoint which lets users search for commits based on various conditions. GitHub announcement - https://developer.github.com/changes/2017-01-05-commit-search-api/ Docs - https://developer.github.com/v3/search/#search-commits Fixes #508. --- github/github.go | 3 +++ github/search.go | 36 ++++++++++++++++++++++++++++++++++++ github/search_test.go | 31 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/github/github.go b/github/github.go index 8f9dde6235c..fd3f4651275 100644 --- a/github/github.go +++ b/github/github.go @@ -93,6 +93,9 @@ const ( // https://developer.github.com/changes/2016-11-28-preview-org-membership/ mediaTypeOrgMembershipPreview = "application/vnd.github.korra-preview+json" + + // https://developer.github.com/changes/2017-01-05-commit-search-api/ + mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json" ) // A Client manages communication with the GitHub API. diff --git a/github/search.go b/github/search.go index 579a57d7f41..e039fa28556 100644 --- a/github/search.go +++ b/github/search.go @@ -21,6 +21,7 @@ type SearchService service type SearchOptions struct { // How to sort the search results. Possible values are: // - for repositories: stars, fork, updated + // - for commits: author-date, committer-date // - for code: indexed // - for issues: comments, created, updated // - for users: followers, repositories, joined @@ -54,6 +55,36 @@ func (s *SearchService) Repositories(query string, opt *SearchOptions) (*Reposit return result, resp, err } +type SingleCommitResult struct { + Hash *string `json:"hash,omitempty"` + Message *string `json:"message,omitempty"` + AuthorID int `json:"author_id,omitempty"` + AuthorName *string `json:"author_name,omitempty"` + AuthorEmail *string `json:"author_email,omitempty"` + AuthorDate *string `json:"author_date,omitempty"` + CommitterID int `json:"committer_id,omitempty"` + CommitterName *string `json:"committer_name,omitempty"` + CommitterEmail *string `json:"committer_email,omitempty"` + CommitterDate *string `json:"committer_date,omitempty"` + Repository *Repository `json:"repository,omitempty"` +} + +// CommitsSearchResult represents the result of a commits search. +type CommitsSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Commits []SingleCommitResult `json:"items,omitempty"` +} + +// Commits searches commits via various criteria. +// +// GitHub API Docs: https://developer.github.com/v3/search/#search-commits +func (s *SearchService) Commits(query string, opt *SearchOptions) (*CommitsSearchResult, *Response, error) { + result := new(CommitsSearchResult) + resp, err := s.search("commits", query, opt, result) + return result, resp, err +} + // IssuesSearchResult represents the result of an issues search. type IssuesSearchResult struct { Total *int `json:"total_count,omitempty"` @@ -150,6 +181,11 @@ func (s *SearchService) search(searchType string, query string, opt *SearchOptio return nil, err } + if searchType == "commits" { + // Accept header for search commits preview endpoint + req.Header.Set("Accept", mediaTypeCommitSearchPreview) + } + if opt != nil && opt.TextMatch { // Accept header defaults to "application/vnd.github.v3+json" // We change it here to fetch back text-match metadata diff --git a/github/search_test.go b/github/search_test.go index 4626de0ea0e..6305066396e 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -46,6 +46,37 @@ func TestSearchService_Repositories(t *testing.T) { } } +func TestSearchService_Commits(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/search/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "author-date", + "order": "desc", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"hash":"random_hash1"},{"hash":"random_hash2"}]}`) + }) + + opts := &SearchOptions{Sort: "author-date", Order: "desc"} + result, _, err := client.Search.Commits("blah", opts) + if err != nil { + t.Errorf("Search.Commits returned error: %v", err) + } + + want := &CommitsSearchResult{ + Total: Int(4), + IncompleteResults: Bool(false), + Commits: []SingleCommitResult{{Hash: String("random_hash1")}, {Hash: String("random_hash2")}}, + } + if !reflect.DeepEqual(result, want) { + t.Errorf("Search.Commits returned %+v, want %+v", result, want) + } +} + func TestSearchService_Issues(t *testing.T) { setup() defer teardown() From 3479b356314037210f81850f75de381c0f2b2534 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 17 Jan 2017 20:13:33 +0100 Subject: [PATCH 2/8] Fix gofmt issue with spacing between parameters --- github/search_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github/search_test.go b/github/search_test.go index 6305066396e..0f599f74002 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -53,9 +53,9 @@ func TestSearchService_Commits(t *testing.T) { mux.HandleFunc("/search/commits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ - "q": "blah", - "sort": "author-date", - "order": "desc", + "q": "blah", + "sort": "author-date", + "order": "desc", }) fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"hash":"random_hash1"},{"hash":"random_hash2"}]}`) From fda8391da0f32e751d9adb120dc80d2aaa58cca1 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 17 Jan 2017 20:34:10 +0100 Subject: [PATCH 3/8] Fix: change int to *int --- github/search.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github/search.go b/github/search.go index e039fa28556..6205bd97e4f 100644 --- a/github/search.go +++ b/github/search.go @@ -58,11 +58,11 @@ func (s *SearchService) Repositories(query string, opt *SearchOptions) (*Reposit type SingleCommitResult struct { Hash *string `json:"hash,omitempty"` Message *string `json:"message,omitempty"` - AuthorID int `json:"author_id,omitempty"` + AuthorID *int `json:"author_id,omitempty"` AuthorName *string `json:"author_name,omitempty"` AuthorEmail *string `json:"author_email,omitempty"` AuthorDate *string `json:"author_date,omitempty"` - CommitterID int `json:"committer_id,omitempty"` + CommitterID *int `json:"committer_id,omitempty"` CommitterName *string `json:"committer_name,omitempty"` CommitterEmail *string `json:"committer_email,omitempty"` CommitterDate *string `json:"committer_date,omitempty"` From f61ac6147c4283a57d0e066d0cd551ac70a3c48f Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 17 Jan 2017 20:37:11 +0100 Subject: [PATCH 4/8] Fix golint issue - Add documentation for struct --- github/search.go | 1 + 1 file changed, 1 insertion(+) diff --git a/github/search.go b/github/search.go index 6205bd97e4f..857627b2dbf 100644 --- a/github/search.go +++ b/github/search.go @@ -55,6 +55,7 @@ func (s *SearchService) Repositories(query string, opt *SearchOptions) (*Reposit return result, resp, err } +// SingleCommitResult represents a commit object as returned in commit search endpoint response. type SingleCommitResult struct { Hash *string `json:"hash,omitempty"` Message *string `json:"message,omitempty"` From 34fd3f012863f95f52939ed38aa1de8ccf01f34c Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 22 Jan 2017 13:04:54 +0100 Subject: [PATCH 5/8] Fix minor issues with commits search support - Make code consistent with the current code. - Simplified logic for setting Accept headers. - Changed AuthorDate and CommitterDate type from *string to *Timestamp. --- github/search.go | 40 ++++++++++++++++++++-------------------- github/search_test.go | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/github/search.go b/github/search.go index 857627b2dbf..0a3dc6080b9 100644 --- a/github/search.go +++ b/github/search.go @@ -55,27 +55,28 @@ func (s *SearchService) Repositories(query string, opt *SearchOptions) (*Reposit return result, resp, err } -// SingleCommitResult represents a commit object as returned in commit search endpoint response. -type SingleCommitResult struct { +// CommitsSearchResult represents the result of a commits search. +type CommitsSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Commits []CommitResult `json:"items,omitempty"` +} + +// CommitResult represents a commit object as returned in commit search endpoint response. +type CommitResult struct { Hash *string `json:"hash,omitempty"` Message *string `json:"message,omitempty"` AuthorID *int `json:"author_id,omitempty"` AuthorName *string `json:"author_name,omitempty"` AuthorEmail *string `json:"author_email,omitempty"` - AuthorDate *string `json:"author_date,omitempty"` + AuthorDate *Timestamp `json:"author_date,omitempty"` CommitterID *int `json:"committer_id,omitempty"` CommitterName *string `json:"committer_name,omitempty"` CommitterEmail *string `json:"committer_email,omitempty"` - CommitterDate *string `json:"committer_date,omitempty"` + CommitterDate *Timestamp `json:"committer_date,omitempty"` Repository *Repository `json:"repository,omitempty"` } -// CommitsSearchResult represents the result of a commits search. -type CommitsSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Commits []SingleCommitResult `json:"items,omitempty"` -} // Commits searches commits via various criteria. // @@ -168,7 +169,7 @@ func (s *SearchService) Code(query string, opt *SearchOptions) (*CodeSearchResul } // Helper function that executes search queries against different -// GitHub search types (repositories, code, issues, users) +// GitHub search types (repositories, commits, code, issues, users) func (s *SearchService) search(searchType string, query string, opt *SearchOptions, result interface{}) (*Response, error) { params, err := qs.Values(opt) if err != nil { @@ -182,15 +183,14 @@ func (s *SearchService) search(searchType string, query string, opt *SearchOptio return nil, err } - if searchType == "commits" { - // Accept header for search commits preview endpoint - req.Header.Set("Accept", mediaTypeCommitSearchPreview) - } - - if opt != nil && opt.TextMatch { - // Accept header defaults to "application/vnd.github.v3+json" - // We change it here to fetch back text-match metadata - req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") + switch { + case opt != nil && opt.TextMatch: + // Accept header defaults to "application/vnd.github.v3+json" + // We change it here to fetch back text-match metadata + req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") + case searchType == "commits": + // Accept header for search commits preview endpoint + req.Header.Set("Accept", mediaTypeCommitSearchPreview) } return s.client.Do(req, result) diff --git a/github/search_test.go b/github/search_test.go index 0f599f74002..f95c34dab94 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -70,7 +70,7 @@ func TestSearchService_Commits(t *testing.T) { want := &CommitsSearchResult{ Total: Int(4), IncompleteResults: Bool(false), - Commits: []SingleCommitResult{{Hash: String("random_hash1")}, {Hash: String("random_hash2")}}, + Commits: []CommitResult{{Hash: String("random_hash1")}, {Hash: String("random_hash2")}}, } if !reflect.DeepEqual(result, want) { t.Errorf("Search.Commits returned %+v, want %+v", result, want) From 14f46c8e41ee45341d89b812a96e47283f920cf7 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 22 Jan 2017 13:22:03 +0100 Subject: [PATCH 6/8] Fix indentation for switch case statement --- github/search.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/github/search.go b/github/search.go index 0a3dc6080b9..264d5b00cda 100644 --- a/github/search.go +++ b/github/search.go @@ -77,7 +77,6 @@ type CommitResult struct { Repository *Repository `json:"repository,omitempty"` } - // Commits searches commits via various criteria. // // GitHub API Docs: https://developer.github.com/v3/search/#search-commits @@ -184,13 +183,13 @@ func (s *SearchService) search(searchType string, query string, opt *SearchOptio } switch { - case opt != nil && opt.TextMatch: - // Accept header defaults to "application/vnd.github.v3+json" - // We change it here to fetch back text-match metadata - req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") - case searchType == "commits": - // Accept header for search commits preview endpoint - req.Header.Set("Accept", mediaTypeCommitSearchPreview) + case opt != nil && opt.TextMatch: + // Accept header defaults to "application/vnd.github.v3+json" + // We change it here to fetch back text-match metadata + req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") + case searchType == "commits": + // Accept header for search commits preview endpoint + req.Header.Set("Accept", mediaTypeCommitSearchPreview) } return s.client.Do(req, result) From 111f86f21688dee0e30185443bb3c37136a125a3 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 31 Jan 2017 01:36:12 +0100 Subject: [PATCH 7/8] Minor cleanup of code; minor improvements - Move commits case above the textMatch one. - Add TODO for removing custom header when API fully launches --- github/search.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/github/search.go b/github/search.go index 264d5b00cda..6257c17f5f0 100644 --- a/github/search.go +++ b/github/search.go @@ -183,13 +183,14 @@ func (s *SearchService) search(searchType string, query string, opt *SearchOptio } switch { + case searchType == "commits": + // Accept header for search commits preview endpoint + // TODO: remove custom Accept header when this API fully launches. + req.Header.Set("Accept", mediaTypeCommitSearchPreview) case opt != nil && opt.TextMatch: // Accept header defaults to "application/vnd.github.v3+json" // We change it here to fetch back text-match metadata req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") - case searchType == "commits": - // Accept header for search commits preview endpoint - req.Header.Set("Accept", mediaTypeCommitSearchPreview) } return s.client.Do(req, result) From ba0574cc2a6205e059953fc59c72d96a015e494c Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Tue, 31 Jan 2017 22:50:20 +0100 Subject: [PATCH 8/8] Change slice of values to slice of pointers --- github/search.go | 6 +++--- github/search_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/github/search.go b/github/search.go index 6257c17f5f0..8fd9430110f 100644 --- a/github/search.go +++ b/github/search.go @@ -57,9 +57,9 @@ func (s *SearchService) Repositories(query string, opt *SearchOptions) (*Reposit // CommitsSearchResult represents the result of a commits search. type CommitsSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Commits []CommitResult `json:"items,omitempty"` + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Commits []*CommitResult `json:"items,omitempty"` } // CommitResult represents a commit object as returned in commit search endpoint response. diff --git a/github/search_test.go b/github/search_test.go index f95c34dab94..0427a8bb485 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -70,7 +70,7 @@ func TestSearchService_Commits(t *testing.T) { want := &CommitsSearchResult{ Total: Int(4), IncompleteResults: Bool(false), - Commits: []CommitResult{{Hash: String("random_hash1")}, {Hash: String("random_hash2")}}, + Commits: []*CommitResult{{Hash: String("random_hash1")}, {Hash: String("random_hash2")}}, } if !reflect.DeepEqual(result, want) { t.Errorf("Search.Commits returned %+v, want %+v", result, want)