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

issue.go 7.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2016 The Gogs 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 repo
  5. import (
  6. "fmt"
  7. "strings"
  8. api "code.gitea.io/sdk/gitea"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/context"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/util"
  13. )
  14. // ListIssues list the issues of a repository
  15. func ListIssues(ctx *context.APIContext) {
  16. // swagger:operation GET /repos/{owner}/{repo}/issues issue issueListIssues
  17. // ---
  18. // summary: List a repository's issues
  19. // produces:
  20. // - application/json
  21. // parameters:
  22. // - name: owner
  23. // in: path
  24. // description: owner of the repo
  25. // type: string
  26. // required: true
  27. // - name: repo
  28. // in: path
  29. // description: name of the repo
  30. // type: string
  31. // required: true
  32. // - name: state
  33. // in: query
  34. // description: whether issue is open or closed
  35. // type: string
  36. // - name: page
  37. // in: query
  38. // description: page number of requested issues
  39. // type: integer
  40. // responses:
  41. // "200":
  42. // "$ref": "#/responses/IssueList"
  43. var isClosed util.OptionalBool
  44. switch ctx.Query("state") {
  45. case "closed":
  46. isClosed = util.OptionalBoolTrue
  47. case "all":
  48. isClosed = util.OptionalBoolNone
  49. default:
  50. isClosed = util.OptionalBoolFalse
  51. }
  52. issues, err := models.Issues(&models.IssuesOptions{
  53. RepoID: ctx.Repo.Repository.ID,
  54. Page: ctx.QueryInt("page"),
  55. PageSize: setting.UI.IssuePagingNum,
  56. IsClosed: isClosed,
  57. })
  58. if err != nil {
  59. ctx.Error(500, "Issues", err)
  60. return
  61. }
  62. apiIssues := make([]*api.Issue, len(issues))
  63. for i := range issues {
  64. apiIssues[i] = issues[i].APIFormat()
  65. }
  66. ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, setting.UI.IssuePagingNum)
  67. ctx.JSON(200, &apiIssues)
  68. }
  69. // GetIssue get an issue of a repository
  70. func GetIssue(ctx *context.APIContext) {
  71. // swagger:operation GET /repos/{owner}/{repo}/issues/{id} issue issueGetIssue
  72. // ---
  73. // summary: Get an issue by id
  74. // produces:
  75. // - application/json
  76. // parameters:
  77. // - name: owner
  78. // in: path
  79. // description: owner of the repo
  80. // type: string
  81. // required: true
  82. // - name: repo
  83. // in: path
  84. // description: name of the repo
  85. // type: string
  86. // required: true
  87. // - name: id
  88. // in: path
  89. // description: id of the issue to get
  90. // type: integer
  91. // required: true
  92. // responses:
  93. // "200":
  94. // "$ref": "#/responses/Issue"
  95. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  96. if err != nil {
  97. if models.IsErrIssueNotExist(err) {
  98. ctx.Status(404)
  99. } else {
  100. ctx.Error(500, "GetIssueByIndex", err)
  101. }
  102. return
  103. }
  104. ctx.JSON(200, issue.APIFormat())
  105. }
  106. // CreateIssue create an issue of a repository
  107. func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
  108. // swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue
  109. // ---
  110. // summary: Create an issue
  111. // consumes:
  112. // - application/json
  113. // produces:
  114. // - application/json
  115. // parameters:
  116. // - name: owner
  117. // in: path
  118. // description: owner of the repo
  119. // type: string
  120. // required: true
  121. // - name: repo
  122. // in: path
  123. // description: name of the repo
  124. // type: string
  125. // required: true
  126. // - name: body
  127. // in: body
  128. // schema:
  129. // "$ref": "#/definitions/CreateIssueOption"
  130. // responses:
  131. // "201":
  132. // "$ref": "#/responses/Issue"
  133. issue := &models.Issue{
  134. RepoID: ctx.Repo.Repository.ID,
  135. Title: form.Title,
  136. PosterID: ctx.User.ID,
  137. Poster: ctx.User,
  138. Content: form.Body,
  139. }
  140. if ctx.Repo.IsWriter() {
  141. if len(form.Assignee) > 0 {
  142. assignee, err := models.GetUserByName(form.Assignee)
  143. if err != nil {
  144. if models.IsErrUserNotExist(err) {
  145. ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", form.Assignee))
  146. } else {
  147. ctx.Error(500, "GetUserByName", err)
  148. }
  149. return
  150. }
  151. issue.AssigneeID = assignee.ID
  152. }
  153. issue.MilestoneID = form.Milestone
  154. } else {
  155. form.Labels = nil
  156. }
  157. if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil {
  158. ctx.Error(500, "NewIssue", err)
  159. return
  160. }
  161. if form.Closed {
  162. if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, true); err != nil {
  163. ctx.Error(500, "ChangeStatus", err)
  164. return
  165. }
  166. }
  167. // Refetch from database to assign some automatic values
  168. var err error
  169. issue, err = models.GetIssueByID(issue.ID)
  170. if err != nil {
  171. ctx.Error(500, "GetIssueByID", err)
  172. return
  173. }
  174. ctx.JSON(201, issue.APIFormat())
  175. }
  176. // EditIssue modify an issue of a repository
  177. func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
  178. // swagger:operation PATCH /repos/{owner}/{repo}/issues/{id} issue issueEditIssue
  179. // ---
  180. // summary: Edit an issue
  181. // consumes:
  182. // - application/json
  183. // produces:
  184. // - application/json
  185. // parameters:
  186. // - name: owner
  187. // in: path
  188. // description: owner of the repo
  189. // type: string
  190. // required: true
  191. // - name: repo
  192. // in: path
  193. // description: name of the repo
  194. // type: string
  195. // required: true
  196. // - name: id
  197. // in: path
  198. // description: id of the issue to edit
  199. // type: integer
  200. // required: true
  201. // - name: body
  202. // in: body
  203. // schema:
  204. // "$ref": "#/definitions/EditIssueOption"
  205. // responses:
  206. // "201":
  207. // "$ref": "#/responses/Issue"
  208. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  209. if err != nil {
  210. if models.IsErrIssueNotExist(err) {
  211. ctx.Status(404)
  212. } else {
  213. ctx.Error(500, "GetIssueByIndex", err)
  214. }
  215. return
  216. }
  217. if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() {
  218. ctx.Status(403)
  219. return
  220. }
  221. if len(form.Title) > 0 {
  222. issue.Title = form.Title
  223. }
  224. if form.Body != nil {
  225. issue.Content = *form.Body
  226. }
  227. if ctx.Repo.IsWriter() && form.Assignee != nil &&
  228. (issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(*form.Assignee)) {
  229. if len(*form.Assignee) == 0 {
  230. issue.AssigneeID = 0
  231. } else {
  232. assignee, err := models.GetUserByName(*form.Assignee)
  233. if err != nil {
  234. if models.IsErrUserNotExist(err) {
  235. ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee))
  236. } else {
  237. ctx.Error(500, "GetUserByName", err)
  238. }
  239. return
  240. }
  241. issue.AssigneeID = assignee.ID
  242. }
  243. if err = models.UpdateIssueUserByAssignee(issue); err != nil {
  244. ctx.Error(500, "UpdateIssueUserByAssignee", err)
  245. return
  246. }
  247. }
  248. if ctx.Repo.IsWriter() && form.Milestone != nil &&
  249. issue.MilestoneID != *form.Milestone {
  250. oldMilestoneID := issue.MilestoneID
  251. issue.MilestoneID = *form.Milestone
  252. if err = models.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil {
  253. ctx.Error(500, "ChangeMilestoneAssign", err)
  254. return
  255. }
  256. }
  257. if err = models.UpdateIssue(issue); err != nil {
  258. ctx.Error(500, "UpdateIssue", err)
  259. return
  260. }
  261. if form.State != nil {
  262. if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil {
  263. ctx.Error(500, "ChangeStatus", err)
  264. return
  265. }
  266. }
  267. // Refetch from database to assign some automatic values
  268. issue, err = models.GetIssueByID(issue.ID)
  269. if err != nil {
  270. ctx.Error(500, "GetIssueByID", err)
  271. return
  272. }
  273. ctx.JSON(201, issue.APIFormat())
  274. }