search.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2013 The go-github AUTHORS. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package github
  6. import (
  7. "context"
  8. "fmt"
  9. qs "github.com/google/go-querystring/query"
  10. )
  11. // SearchService provides access to the search related functions
  12. // in the GitHub API.
  13. //
  14. // Each method takes a query string defining the search keywords and any search qualifiers.
  15. // For example, when searching issues, the query "gopher is:issue language:go" will search
  16. // for issues containing the word "gopher" in Go repositories. The method call
  17. // opts := &github.SearchOptions{Sort: "created", Order: "asc"}
  18. // cl.Search.Issues(ctx, "gopher is:issue language:go", opts)
  19. // will search for such issues, sorting by creation date in ascending order
  20. // (i.e., oldest first).
  21. //
  22. // GitHub API docs: https://developer.github.com/v3/search/
  23. type SearchService service
  24. // SearchOptions specifies optional parameters to the SearchService methods.
  25. type SearchOptions struct {
  26. // How to sort the search results. Possible values are:
  27. // - for repositories: stars, fork, updated
  28. // - for commits: author-date, committer-date
  29. // - for code: indexed
  30. // - for issues: comments, created, updated
  31. // - for users: followers, repositories, joined
  32. //
  33. // Default is to sort by best match.
  34. Sort string `url:"sort,omitempty"`
  35. // Sort order if sort parameter is provided. Possible values are: asc,
  36. // desc. Default is desc.
  37. Order string `url:"order,omitempty"`
  38. // Whether to retrieve text match metadata with a query
  39. TextMatch bool `url:"-"`
  40. ListOptions
  41. }
  42. // RepositoriesSearchResult represents the result of a repositories search.
  43. type RepositoriesSearchResult struct {
  44. Total *int `json:"total_count,omitempty"`
  45. IncompleteResults *bool `json:"incomplete_results,omitempty"`
  46. Repositories []Repository `json:"items,omitempty"`
  47. }
  48. // Repositories searches repositories via various criteria.
  49. //
  50. // GitHub API docs: https://developer.github.com/v3/search/#search-repositories
  51. func (s *SearchService) Repositories(ctx context.Context, query string, opt *SearchOptions) (*RepositoriesSearchResult, *Response, error) {
  52. result := new(RepositoriesSearchResult)
  53. resp, err := s.search(ctx, "repositories", query, opt, result)
  54. return result, resp, err
  55. }
  56. // CommitsSearchResult represents the result of a commits search.
  57. type CommitsSearchResult struct {
  58. Total *int `json:"total_count,omitempty"`
  59. IncompleteResults *bool `json:"incomplete_results,omitempty"`
  60. Commits []*CommitResult `json:"items,omitempty"`
  61. }
  62. // CommitResult represents a commit object as returned in commit search endpoint response.
  63. type CommitResult struct {
  64. SHA *string `json:"sha,omitempty"`
  65. Commit *Commit `json:"commit,omitempty"`
  66. Author *User `json:"author,omitempty"`
  67. Committer *User `json:"committer,omitempty"`
  68. Parents []*Commit `json:"parents,omitempty"`
  69. HTMLURL *string `json:"html_url,omitempty"`
  70. URL *string `json:"url,omitempty"`
  71. CommentsURL *string `json:"comments_url,omitempty"`
  72. Repository *Repository `json:"repository,omitempty"`
  73. Score *float64 `json:"score,omitempty"`
  74. }
  75. // Commits searches commits via various criteria.
  76. //
  77. // GitHub API docs: https://developer.github.com/v3/search/#search-commits
  78. func (s *SearchService) Commits(ctx context.Context, query string, opt *SearchOptions) (*CommitsSearchResult, *Response, error) {
  79. result := new(CommitsSearchResult)
  80. resp, err := s.search(ctx, "commits", query, opt, result)
  81. return result, resp, err
  82. }
  83. // IssuesSearchResult represents the result of an issues search.
  84. type IssuesSearchResult struct {
  85. Total *int `json:"total_count,omitempty"`
  86. IncompleteResults *bool `json:"incomplete_results,omitempty"`
  87. Issues []Issue `json:"items,omitempty"`
  88. }
  89. // Issues searches issues via various criteria.
  90. //
  91. // GitHub API docs: https://developer.github.com/v3/search/#search-issues
  92. func (s *SearchService) Issues(ctx context.Context, query string, opt *SearchOptions) (*IssuesSearchResult, *Response, error) {
  93. result := new(IssuesSearchResult)
  94. resp, err := s.search(ctx, "issues", query, opt, result)
  95. return result, resp, err
  96. }
  97. // UsersSearchResult represents the result of a users search.
  98. type UsersSearchResult struct {
  99. Total *int `json:"total_count,omitempty"`
  100. IncompleteResults *bool `json:"incomplete_results,omitempty"`
  101. Users []User `json:"items,omitempty"`
  102. }
  103. // Users searches users via various criteria.
  104. //
  105. // GitHub API docs: https://developer.github.com/v3/search/#search-users
  106. func (s *SearchService) Users(ctx context.Context, query string, opt *SearchOptions) (*UsersSearchResult, *Response, error) {
  107. result := new(UsersSearchResult)
  108. resp, err := s.search(ctx, "users", query, opt, result)
  109. return result, resp, err
  110. }
  111. // Match represents a single text match.
  112. type Match struct {
  113. Text *string `json:"text,omitempty"`
  114. Indices []int `json:"indices,omitempty"`
  115. }
  116. // TextMatch represents a text match for a SearchResult
  117. type TextMatch struct {
  118. ObjectURL *string `json:"object_url,omitempty"`
  119. ObjectType *string `json:"object_type,omitempty"`
  120. Property *string `json:"property,omitempty"`
  121. Fragment *string `json:"fragment,omitempty"`
  122. Matches []Match `json:"matches,omitempty"`
  123. }
  124. func (tm TextMatch) String() string {
  125. return Stringify(tm)
  126. }
  127. // CodeSearchResult represents the result of a code search.
  128. type CodeSearchResult struct {
  129. Total *int `json:"total_count,omitempty"`
  130. IncompleteResults *bool `json:"incomplete_results,omitempty"`
  131. CodeResults []CodeResult `json:"items,omitempty"`
  132. }
  133. // CodeResult represents a single search result.
  134. type CodeResult struct {
  135. Name *string `json:"name,omitempty"`
  136. Path *string `json:"path,omitempty"`
  137. SHA *string `json:"sha,omitempty"`
  138. HTMLURL *string `json:"html_url,omitempty"`
  139. Repository *Repository `json:"repository,omitempty"`
  140. TextMatches []TextMatch `json:"text_matches,omitempty"`
  141. }
  142. func (c CodeResult) String() string {
  143. return Stringify(c)
  144. }
  145. // Code searches code via various criteria.
  146. //
  147. // GitHub API docs: https://developer.github.com/v3/search/#search-code
  148. func (s *SearchService) Code(ctx context.Context, query string, opt *SearchOptions) (*CodeSearchResult, *Response, error) {
  149. result := new(CodeSearchResult)
  150. resp, err := s.search(ctx, "code", query, opt, result)
  151. return result, resp, err
  152. }
  153. // Helper function that executes search queries against different
  154. // GitHub search types (repositories, commits, code, issues, users)
  155. func (s *SearchService) search(ctx context.Context, searchType string, query string, opt *SearchOptions, result interface{}) (*Response, error) {
  156. params, err := qs.Values(opt)
  157. if err != nil {
  158. return nil, err
  159. }
  160. params.Set("q", query)
  161. u := fmt.Sprintf("search/%s?%s", searchType, params.Encode())
  162. req, err := s.client.NewRequest("GET", u, nil)
  163. if err != nil {
  164. return nil, err
  165. }
  166. switch {
  167. case searchType == "commits":
  168. // Accept header for search commits preview endpoint
  169. // TODO: remove custom Accept header when this API fully launches.
  170. req.Header.Set("Accept", mediaTypeCommitSearchPreview)
  171. case searchType == "repositories":
  172. // Accept header for search repositories based on topics preview endpoint
  173. // TODO: remove custom Accept header when this API fully launches.
  174. req.Header.Set("Accept", mediaTypeTopicsPreview)
  175. case opt != nil && opt.TextMatch:
  176. // Accept header defaults to "application/vnd.github.v3+json"
  177. // We change it here to fetch back text-match metadata
  178. req.Header.Set("Accept", "application/vnd.github.v3.text-match+json")
  179. }
  180. return s.client.Do(ctx, req, result)
  181. }