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.

issue.go 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package convert
  5. import (
  6. "fmt"
  7. "strings"
  8. "code.gitea.io/gitea/models"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/setting"
  11. api "code.gitea.io/gitea/modules/structs"
  12. )
  13. // ToAPIIssue converts an Issue to API format
  14. // it assumes some fields assigned with values:
  15. // Required - Poster, Labels,
  16. // Optional - Milestone, Assignee, PullRequest
  17. func ToAPIIssue(issue *models.Issue) *api.Issue {
  18. if err := issue.LoadLabels(); err != nil {
  19. return &api.Issue{}
  20. }
  21. if err := issue.LoadPoster(); err != nil {
  22. return &api.Issue{}
  23. }
  24. if err := issue.LoadRepo(); err != nil {
  25. return &api.Issue{}
  26. }
  27. if err := issue.Repo.GetOwner(); err != nil {
  28. return &api.Issue{}
  29. }
  30. apiIssue := &api.Issue{
  31. ID: issue.ID,
  32. URL: issue.APIURL(),
  33. HTMLURL: issue.HTMLURL(),
  34. Index: issue.Index,
  35. Poster: ToUser(issue.Poster, nil),
  36. Title: issue.Title,
  37. Body: issue.Content,
  38. Ref: issue.Ref,
  39. Labels: ToLabelList(issue.Labels, issue.Repo, issue.Repo.Owner),
  40. State: issue.State(),
  41. IsLocked: issue.IsLocked,
  42. Comments: issue.NumComments,
  43. Created: issue.CreatedUnix.AsTime(),
  44. Updated: issue.UpdatedUnix.AsTime(),
  45. }
  46. apiIssue.Repo = &api.RepositoryMeta{
  47. ID: issue.Repo.ID,
  48. Name: issue.Repo.Name,
  49. Owner: issue.Repo.OwnerName,
  50. FullName: issue.Repo.FullName(),
  51. }
  52. if issue.ClosedUnix != 0 {
  53. apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
  54. }
  55. if err := issue.LoadMilestone(); err != nil {
  56. return &api.Issue{}
  57. }
  58. if issue.Milestone != nil {
  59. apiIssue.Milestone = ToAPIMilestone(issue.Milestone)
  60. }
  61. if err := issue.LoadAssignees(); err != nil {
  62. return &api.Issue{}
  63. }
  64. if len(issue.Assignees) > 0 {
  65. for _, assignee := range issue.Assignees {
  66. apiIssue.Assignees = append(apiIssue.Assignees, ToUser(assignee, nil))
  67. }
  68. apiIssue.Assignee = ToUser(issue.Assignees[0], nil) // For compatibility, we're keeping the first assignee as `apiIssue.Assignee`
  69. }
  70. if issue.IsPull {
  71. if err := issue.LoadPullRequest(); err != nil {
  72. return &api.Issue{}
  73. }
  74. apiIssue.PullRequest = &api.PullRequestMeta{
  75. HasMerged: issue.PullRequest.HasMerged,
  76. }
  77. if issue.PullRequest.HasMerged {
  78. apiIssue.PullRequest.Merged = issue.PullRequest.MergedUnix.AsTimePtr()
  79. }
  80. }
  81. if issue.DeadlineUnix != 0 {
  82. apiIssue.Deadline = issue.DeadlineUnix.AsTimePtr()
  83. }
  84. return apiIssue
  85. }
  86. // ToAPIIssueList converts an IssueList to API format
  87. func ToAPIIssueList(il models.IssueList) []*api.Issue {
  88. result := make([]*api.Issue, len(il))
  89. for i := range il {
  90. result[i] = ToAPIIssue(il[i])
  91. }
  92. return result
  93. }
  94. // ToTrackedTime converts TrackedTime to API format
  95. func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) {
  96. apiT = &api.TrackedTime{
  97. ID: t.ID,
  98. IssueID: t.IssueID,
  99. UserID: t.UserID,
  100. UserName: t.User.Name,
  101. Time: t.Time,
  102. Created: t.Created,
  103. }
  104. if t.Issue != nil {
  105. apiT.Issue = ToAPIIssue(t.Issue)
  106. }
  107. if t.User != nil {
  108. apiT.UserName = t.User.Name
  109. }
  110. return
  111. }
  112. // ToStopWatches convert Stopwatch list to api.StopWatches
  113. func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) {
  114. result := api.StopWatches(make([]api.StopWatch, 0, len(sws)))
  115. issueCache := make(map[int64]*models.Issue)
  116. repoCache := make(map[int64]*models.Repository)
  117. var (
  118. issue *models.Issue
  119. repo *models.Repository
  120. ok bool
  121. err error
  122. )
  123. for _, sw := range sws {
  124. issue, ok = issueCache[sw.IssueID]
  125. if !ok {
  126. issue, err = models.GetIssueByID(sw.IssueID)
  127. if err != nil {
  128. return nil, err
  129. }
  130. }
  131. repo, ok = repoCache[issue.RepoID]
  132. if !ok {
  133. repo, err = models.GetRepositoryByID(issue.RepoID)
  134. if err != nil {
  135. return nil, err
  136. }
  137. }
  138. result = append(result, api.StopWatch{
  139. Created: sw.CreatedUnix.AsTime(),
  140. Seconds: sw.Seconds(),
  141. Duration: sw.Duration(),
  142. IssueIndex: issue.Index,
  143. IssueTitle: issue.Title,
  144. RepoOwnerName: repo.OwnerName,
  145. RepoName: repo.Name,
  146. })
  147. }
  148. return result, nil
  149. }
  150. // ToTrackedTimeList converts TrackedTimeList to API format
  151. func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList {
  152. result := make([]*api.TrackedTime, 0, len(tl))
  153. for _, t := range tl {
  154. result = append(result, ToTrackedTime(t))
  155. }
  156. return result
  157. }
  158. // ToLabel converts Label to API format
  159. func ToLabel(label *models.Label, repo *models.Repository, org *models.User) *api.Label {
  160. result := &api.Label{
  161. ID: label.ID,
  162. Name: label.Name,
  163. Color: strings.TrimLeft(label.Color, "#"),
  164. Description: label.Description,
  165. }
  166. // calculate URL
  167. if label.BelongsToRepo() && repo != nil {
  168. if repo != nil {
  169. result.URL = fmt.Sprintf("%s/labels/%d", repo.APIURL(), label.ID)
  170. } else {
  171. log.Error("ToLabel did not get repo to calculate url for label with id '%d'", label.ID)
  172. }
  173. } else { // BelongsToOrg
  174. if org != nil {
  175. result.URL = fmt.Sprintf("%sapi/v1/orgs/%s/labels/%d", setting.AppURL, org.Name, label.ID)
  176. } else {
  177. log.Error("ToLabel did not get org to calculate url for label with id '%d'", label.ID)
  178. }
  179. }
  180. return result
  181. }
  182. // ToLabelList converts list of Label to API format
  183. func ToLabelList(labels []*models.Label, repo *models.Repository, org *models.User) []*api.Label {
  184. result := make([]*api.Label, len(labels))
  185. for i := range labels {
  186. result[i] = ToLabel(labels[i], repo, org)
  187. }
  188. return result
  189. }
  190. // ToAPIMilestone converts Milestone into API Format
  191. func ToAPIMilestone(m *models.Milestone) *api.Milestone {
  192. apiMilestone := &api.Milestone{
  193. ID: m.ID,
  194. State: m.State(),
  195. Title: m.Name,
  196. Description: m.Content,
  197. OpenIssues: m.NumOpenIssues,
  198. ClosedIssues: m.NumClosedIssues,
  199. Created: m.CreatedUnix.AsTime(),
  200. Updated: m.UpdatedUnix.AsTimePtr(),
  201. }
  202. if m.IsClosed {
  203. apiMilestone.Closed = m.ClosedDateUnix.AsTimePtr()
  204. }
  205. if m.DeadlineUnix.Year() < 9999 {
  206. apiMilestone.Deadline = m.DeadlineUnix.AsTimePtr()
  207. }
  208. return apiMilestone
  209. }