本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
12KB

  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. "strings"
  10. "time"
  11. )
  12. // IssuesService handles communication with the issue related
  13. // methods of the GitHub API.
  14. //
  15. // GitHub API docs: https://developer.github.com/v3/issues/
  16. type IssuesService service
  17. // Issue represents a GitHub issue on a repository.
  18. //
  19. // Note: As far as the GitHub API is concerned, every pull request is an issue,
  20. // but not every issue is a pull request. Some endpoints, events, and webhooks
  21. // may also return pull requests via this struct. If PullRequestLinks is nil,
  22. // this is an issue, and if PullRequestLinks is not nil, this is a pull request.
  23. // The IsPullRequest helper method can be used to check that.
  24. type Issue struct {
  25. ID *int64 `json:"id,omitempty"`
  26. Number *int `json:"number,omitempty"`
  27. State *string `json:"state,omitempty"`
  28. Locked *bool `json:"locked,omitempty"`
  29. Title *string `json:"title,omitempty"`
  30. Body *string `json:"body,omitempty"`
  31. User *User `json:"user,omitempty"`
  32. Labels []Label `json:"labels,omitempty"`
  33. Assignee *User `json:"assignee,omitempty"`
  34. Comments *int `json:"comments,omitempty"`
  35. ClosedAt *time.Time `json:"closed_at,omitempty"`
  36. CreatedAt *time.Time `json:"created_at,omitempty"`
  37. UpdatedAt *time.Time `json:"updated_at,omitempty"`
  38. ClosedBy *User `json:"closed_by,omitempty"`
  39. URL *string `json:"url,omitempty"`
  40. HTMLURL *string `json:"html_url,omitempty"`
  41. CommentsURL *string `json:"comments_url,omitempty"`
  42. EventsURL *string `json:"events_url,omitempty"`
  43. LabelsURL *string `json:"labels_url,omitempty"`
  44. RepositoryURL *string `json:"repository_url,omitempty"`
  45. Milestone *Milestone `json:"milestone,omitempty"`
  46. PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`
  47. Repository *Repository `json:"repository,omitempty"`
  48. Reactions *Reactions `json:"reactions,omitempty"`
  49. Assignees []*User `json:"assignees,omitempty"`
  50. NodeID *string `json:"node_id,omitempty"`
  51. // TextMatches is only populated from search results that request text matches
  52. // See: search.go and https://developer.github.com/v3/search/#text-match-metadata
  53. TextMatches []TextMatch `json:"text_matches,omitempty"`
  54. // ActiveLockReason is populated only when LockReason is provided while locking the issue.
  55. // Possible values are: "off-topic", "too heated", "resolved", and "spam".
  56. ActiveLockReason *string `json:"active_lock_reason,omitempty"`
  57. }
  58. func (i Issue) String() string {
  59. return Stringify(i)
  60. }
  61. // IsPullRequest reports whether the issue is also a pull request. It uses the
  62. // method recommended by GitHub's API documentation, which is to check whether
  63. // PullRequestLinks is non-nil.
  64. func (i Issue) IsPullRequest() bool {
  65. return i.PullRequestLinks != nil
  66. }
  67. // IssueRequest represents a request to create/edit an issue.
  68. // It is separate from Issue above because otherwise Labels
  69. // and Assignee fail to serialize to the correct JSON.
  70. type IssueRequest struct {
  71. Title *string `json:"title,omitempty"`
  72. Body *string `json:"body,omitempty"`
  73. Labels *[]string `json:"labels,omitempty"`
  74. Assignee *string `json:"assignee,omitempty"`
  75. State *string `json:"state,omitempty"`
  76. Milestone *int `json:"milestone,omitempty"`
  77. Assignees *[]string `json:"assignees,omitempty"`
  78. }
  79. // IssueListOptions specifies the optional parameters to the IssuesService.List
  80. // and IssuesService.ListByOrg methods.
  81. type IssueListOptions struct {
  82. // Filter specifies which issues to list. Possible values are: assigned,
  83. // created, mentioned, subscribed, all. Default is "assigned".
  84. Filter string `url:"filter,omitempty"`
  85. // State filters issues based on their state. Possible values are: open,
  86. // closed, all. Default is "open".
  87. State string `url:"state,omitempty"`
  88. // Labels filters issues based on their label.
  89. Labels []string `url:"labels,comma,omitempty"`
  90. // Sort specifies how to sort issues. Possible values are: created, updated,
  91. // and comments. Default value is "created".
  92. Sort string `url:"sort,omitempty"`
  93. // Direction in which to sort issues. Possible values are: asc, desc.
  94. // Default is "desc".
  95. Direction string `url:"direction,omitempty"`
  96. // Since filters issues by time.
  97. Since time.Time `url:"since,omitempty"`
  98. ListOptions
  99. }
  100. // PullRequestLinks object is added to the Issue object when it's an issue included
  101. // in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR.
  102. type PullRequestLinks struct {
  103. URL *string `json:"url,omitempty"`
  104. HTMLURL *string `json:"html_url,omitempty"`
  105. DiffURL *string `json:"diff_url,omitempty"`
  106. PatchURL *string `json:"patch_url,omitempty"`
  107. }
  108. // List the issues for the authenticated user. If all is true, list issues
  109. // across all the user's visible repositories including owned, member, and
  110. // organization repositories; if false, list only owned and member
  111. // repositories.
  112. //
  113. // GitHub API docs: https://developer.github.com/v3/issues/#list-issues
  114. func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) {
  115. var u string
  116. if all {
  117. u = "issues"
  118. } else {
  119. u = "user/issues"
  120. }
  121. return s.listIssues(ctx, u, opt)
  122. }
  123. // ListByOrg fetches the issues in the specified organization for the
  124. // authenticated user.
  125. //
  126. // GitHub API docs: https://developer.github.com/v3/issues/#list-issues
  127. func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) {
  128. u := fmt.Sprintf("orgs/%v/issues", org)
  129. return s.listIssues(ctx, u, opt)
  130. }
  131. func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) {
  132. u, err := addOptions(u, opt)
  133. if err != nil {
  134. return nil, nil, err
  135. }
  136. req, err := s.client.NewRequest("GET", u, nil)
  137. if err != nil {
  138. return nil, nil, err
  139. }
  140. // TODO: remove custom Accept headers when APIs fully launch.
  141. acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
  142. req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
  143. var issues []*Issue
  144. resp, err := s.client.Do(ctx, req, &issues)
  145. if err != nil {
  146. return nil, resp, err
  147. }
  148. return issues, resp, nil
  149. }
  150. // IssueListByRepoOptions specifies the optional parameters to the
  151. // IssuesService.ListByRepo method.
  152. type IssueListByRepoOptions struct {
  153. // Milestone limits issues for the specified milestone. Possible values are
  154. // a milestone number, "none" for issues with no milestone, "*" for issues
  155. // with any milestone.
  156. Milestone string `url:"milestone,omitempty"`
  157. // State filters issues based on their state. Possible values are: open,
  158. // closed, all. Default is "open".
  159. State string `url:"state,omitempty"`
  160. // Assignee filters issues based on their assignee. Possible values are a
  161. // user name, "none" for issues that are not assigned, "*" for issues with
  162. // any assigned user.
  163. Assignee string `url:"assignee,omitempty"`
  164. // Creator filters issues based on their creator.
  165. Creator string `url:"creator,omitempty"`
  166. // Mentioned filters issues to those mentioned a specific user.
  167. Mentioned string `url:"mentioned,omitempty"`
  168. // Labels filters issues based on their label.
  169. Labels []string `url:"labels,omitempty,comma"`
  170. // Sort specifies how to sort issues. Possible values are: created, updated,
  171. // and comments. Default value is "created".
  172. Sort string `url:"sort,omitempty"`
  173. // Direction in which to sort issues. Possible values are: asc, desc.
  174. // Default is "desc".
  175. Direction string `url:"direction,omitempty"`
  176. // Since filters issues by time.
  177. Since time.Time `url:"since,omitempty"`
  178. ListOptions
  179. }
  180. // ListByRepo lists the issues for the specified repository.
  181. //
  182. // GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository
  183. func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) {
  184. u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
  185. u, err := addOptions(u, opt)
  186. if err != nil {
  187. return nil, nil, err
  188. }
  189. req, err := s.client.NewRequest("GET", u, nil)
  190. if err != nil {
  191. return nil, nil, err
  192. }
  193. // TODO: remove custom Accept headers when APIs fully launch.
  194. acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview}
  195. req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
  196. var issues []*Issue
  197. resp, err := s.client.Do(ctx, req, &issues)
  198. if err != nil {
  199. return nil, resp, err
  200. }
  201. return issues, resp, nil
  202. }
  203. // Get a single issue.
  204. //
  205. // GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue
  206. func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) {
  207. u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
  208. req, err := s.client.NewRequest("GET", u, nil)
  209. if err != nil {
  210. return nil, nil, err
  211. }
  212. // TODO: remove custom Accept headers when APIs fully launch.
  213. acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
  214. req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
  215. issue := new(Issue)
  216. resp, err := s.client.Do(ctx, req, issue)
  217. if err != nil {
  218. return nil, resp, err
  219. }
  220. return issue, resp, nil
  221. }
  222. // Create a new issue on the specified repository.
  223. //
  224. // GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue
  225. func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) {
  226. u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
  227. req, err := s.client.NewRequest("POST", u, issue)
  228. if err != nil {
  229. return nil, nil, err
  230. }
  231. // TODO: remove custom Accept header when this API fully launches.
  232. req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
  233. i := new(Issue)
  234. resp, err := s.client.Do(ctx, req, i)
  235. if err != nil {
  236. return nil, resp, err
  237. }
  238. return i, resp, nil
  239. }
  240. // Edit an issue.
  241. //
  242. // GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue
  243. func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) {
  244. u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
  245. req, err := s.client.NewRequest("PATCH", u, issue)
  246. if err != nil {
  247. return nil, nil, err
  248. }
  249. // TODO: remove custom Accept header when this API fully launches.
  250. req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
  251. i := new(Issue)
  252. resp, err := s.client.Do(ctx, req, i)
  253. if err != nil {
  254. return nil, resp, err
  255. }
  256. return i, resp, nil
  257. }
  258. // LockIssueOptions specifies the optional parameters to the
  259. // IssuesService.Lock method.
  260. type LockIssueOptions struct {
  261. // LockReason specifies the reason to lock this issue.
  262. // Providing a lock reason can help make it clearer to contributors why an issue
  263. // was locked. Possible values are: "off-topic", "too heated", "resolved", and "spam".
  264. LockReason string `json:"lock_reason,omitempty"`
  265. }
  266. // Lock an issue's conversation.
  267. //
  268. // GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue
  269. func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) {
  270. u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
  271. req, err := s.client.NewRequest("PUT", u, opt)
  272. if err != nil {
  273. return nil, err
  274. }
  275. if opt != nil {
  276. req.Header.Set("Accept", mediaTypeLockReasonPreview)
  277. }
  278. return s.client.Do(ctx, req, nil)
  279. }
  280. // Unlock an issue's conversation.
  281. //
  282. // GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue
  283. func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) {
  284. u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
  285. req, err := s.client.NewRequest("DELETE", u, nil)
  286. if err != nil {
  287. return nil, err
  288. }
  289. return s.client.Do(ctx, req, nil)
  290. }
上海开阖软件有限公司 沪ICP备12045867号-1