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_label.go 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package repo
  5. import (
  6. "net/http"
  7. issues_model "code.gitea.io/gitea/models/issues"
  8. api "code.gitea.io/gitea/modules/structs"
  9. "code.gitea.io/gitea/modules/web"
  10. "code.gitea.io/gitea/services/context"
  11. "code.gitea.io/gitea/services/convert"
  12. issue_service "code.gitea.io/gitea/services/issue"
  13. )
  14. // ListIssueLabels list all the labels of an issue
  15. func ListIssueLabels(ctx *context.APIContext) {
  16. // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/labels issue issueGetLabels
  17. // ---
  18. // summary: Get an issue's labels
  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: index
  33. // in: path
  34. // description: index of the issue
  35. // type: integer
  36. // format: int64
  37. // required: true
  38. // responses:
  39. // "200":
  40. // "$ref": "#/responses/LabelList"
  41. // "404":
  42. // "$ref": "#/responses/notFound"
  43. issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  44. if err != nil {
  45. if issues_model.IsErrIssueNotExist(err) {
  46. ctx.NotFound()
  47. } else {
  48. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  49. }
  50. return
  51. }
  52. if err := issue.LoadAttributes(ctx); err != nil {
  53. ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
  54. return
  55. }
  56. ctx.JSON(http.StatusOK, convert.ToLabelList(issue.Labels, ctx.Repo.Repository, ctx.Repo.Owner))
  57. }
  58. // AddIssueLabels add labels for an issue
  59. func AddIssueLabels(ctx *context.APIContext) {
  60. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel
  61. // ---
  62. // summary: Add a label to an issue
  63. // consumes:
  64. // - application/json
  65. // produces:
  66. // - application/json
  67. // parameters:
  68. // - name: owner
  69. // in: path
  70. // description: owner of the repo
  71. // type: string
  72. // required: true
  73. // - name: repo
  74. // in: path
  75. // description: name of the repo
  76. // type: string
  77. // required: true
  78. // - name: index
  79. // in: path
  80. // description: index of the issue
  81. // type: integer
  82. // format: int64
  83. // required: true
  84. // - name: body
  85. // in: body
  86. // schema:
  87. // "$ref": "#/definitions/IssueLabelsOption"
  88. // responses:
  89. // "200":
  90. // "$ref": "#/responses/LabelList"
  91. // "403":
  92. // "$ref": "#/responses/forbidden"
  93. // "404":
  94. // "$ref": "#/responses/notFound"
  95. form := web.GetForm(ctx).(*api.IssueLabelsOption)
  96. issue, labels, err := prepareForReplaceOrAdd(ctx, *form)
  97. if err != nil {
  98. return
  99. }
  100. if err = issue_service.AddLabels(ctx, issue, ctx.Doer, labels); err != nil {
  101. ctx.Error(http.StatusInternalServerError, "AddLabels", err)
  102. return
  103. }
  104. labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID)
  105. if err != nil {
  106. ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err)
  107. return
  108. }
  109. ctx.JSON(http.StatusOK, convert.ToLabelList(labels, ctx.Repo.Repository, ctx.Repo.Owner))
  110. }
  111. // DeleteIssueLabel delete a label for an issue
  112. func DeleteIssueLabel(ctx *context.APIContext) {
  113. // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} issue issueRemoveLabel
  114. // ---
  115. // summary: Remove a label from an issue
  116. // produces:
  117. // - application/json
  118. // parameters:
  119. // - name: owner
  120. // in: path
  121. // description: owner of the repo
  122. // type: string
  123. // required: true
  124. // - name: repo
  125. // in: path
  126. // description: name of the repo
  127. // type: string
  128. // required: true
  129. // - name: index
  130. // in: path
  131. // description: index of the issue
  132. // type: integer
  133. // format: int64
  134. // required: true
  135. // - name: id
  136. // in: path
  137. // description: id of the label to remove
  138. // type: integer
  139. // format: int64
  140. // required: true
  141. // responses:
  142. // "204":
  143. // "$ref": "#/responses/empty"
  144. // "403":
  145. // "$ref": "#/responses/forbidden"
  146. // "404":
  147. // "$ref": "#/responses/notFound"
  148. // "422":
  149. // "$ref": "#/responses/validationError"
  150. issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  151. if err != nil {
  152. if issues_model.IsErrIssueNotExist(err) {
  153. ctx.NotFound()
  154. } else {
  155. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  156. }
  157. return
  158. }
  159. if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
  160. ctx.Status(http.StatusForbidden)
  161. return
  162. }
  163. label, err := issues_model.GetLabelByID(ctx, ctx.ParamsInt64(":id"))
  164. if err != nil {
  165. if issues_model.IsErrLabelNotExist(err) {
  166. ctx.Error(http.StatusUnprocessableEntity, "", err)
  167. } else {
  168. ctx.Error(http.StatusInternalServerError, "GetLabelByID", err)
  169. }
  170. return
  171. }
  172. if err := issue_service.RemoveLabel(ctx, issue, ctx.Doer, label); err != nil {
  173. ctx.Error(http.StatusInternalServerError, "DeleteIssueLabel", err)
  174. return
  175. }
  176. ctx.Status(http.StatusNoContent)
  177. }
  178. // ReplaceIssueLabels replace labels for an issue
  179. func ReplaceIssueLabels(ctx *context.APIContext) {
  180. // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels
  181. // ---
  182. // summary: Replace an issue's labels
  183. // consumes:
  184. // - application/json
  185. // produces:
  186. // - application/json
  187. // parameters:
  188. // - name: owner
  189. // in: path
  190. // description: owner of the repo
  191. // type: string
  192. // required: true
  193. // - name: repo
  194. // in: path
  195. // description: name of the repo
  196. // type: string
  197. // required: true
  198. // - name: index
  199. // in: path
  200. // description: index of the issue
  201. // type: integer
  202. // format: int64
  203. // required: true
  204. // - name: body
  205. // in: body
  206. // schema:
  207. // "$ref": "#/definitions/IssueLabelsOption"
  208. // responses:
  209. // "200":
  210. // "$ref": "#/responses/LabelList"
  211. // "403":
  212. // "$ref": "#/responses/forbidden"
  213. // "404":
  214. // "$ref": "#/responses/notFound"
  215. form := web.GetForm(ctx).(*api.IssueLabelsOption)
  216. issue, labels, err := prepareForReplaceOrAdd(ctx, *form)
  217. if err != nil {
  218. return
  219. }
  220. if err := issue_service.ReplaceLabels(ctx, issue, ctx.Doer, labels); err != nil {
  221. ctx.Error(http.StatusInternalServerError, "ReplaceLabels", err)
  222. return
  223. }
  224. labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID)
  225. if err != nil {
  226. ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err)
  227. return
  228. }
  229. ctx.JSON(http.StatusOK, convert.ToLabelList(labels, ctx.Repo.Repository, ctx.Repo.Owner))
  230. }
  231. // ClearIssueLabels delete all the labels for an issue
  232. func ClearIssueLabels(ctx *context.APIContext) {
  233. // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels issue issueClearLabels
  234. // ---
  235. // summary: Remove all labels from an issue
  236. // produces:
  237. // - application/json
  238. // parameters:
  239. // - name: owner
  240. // in: path
  241. // description: owner of the repo
  242. // type: string
  243. // required: true
  244. // - name: repo
  245. // in: path
  246. // description: name of the repo
  247. // type: string
  248. // required: true
  249. // - name: index
  250. // in: path
  251. // description: index of the issue
  252. // type: integer
  253. // format: int64
  254. // required: true
  255. // responses:
  256. // "204":
  257. // "$ref": "#/responses/empty"
  258. // "403":
  259. // "$ref": "#/responses/forbidden"
  260. // "404":
  261. // "$ref": "#/responses/notFound"
  262. issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  263. if err != nil {
  264. if issues_model.IsErrIssueNotExist(err) {
  265. ctx.NotFound()
  266. } else {
  267. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  268. }
  269. return
  270. }
  271. if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
  272. ctx.Status(http.StatusForbidden)
  273. return
  274. }
  275. if err := issue_service.ClearLabels(ctx, issue, ctx.Doer); err != nil {
  276. ctx.Error(http.StatusInternalServerError, "ClearLabels", err)
  277. return
  278. }
  279. ctx.Status(http.StatusNoContent)
  280. }
  281. func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (*issues_model.Issue, []*issues_model.Label, error) {
  282. issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  283. if err != nil {
  284. if issues_model.IsErrIssueNotExist(err) {
  285. ctx.NotFound()
  286. } else {
  287. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  288. }
  289. return nil, nil, err
  290. }
  291. labels, err := issues_model.GetLabelsByIDs(ctx, form.Labels, "id", "repo_id", "org_id", "name", "exclusive")
  292. if err != nil {
  293. ctx.Error(http.StatusInternalServerError, "GetLabelsByIDs", err)
  294. return nil, nil, err
  295. }
  296. if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
  297. ctx.Status(http.StatusForbidden)
  298. return nil, nil, nil
  299. }
  300. return issue, labels, err
  301. }