code.gitea.io/gitea@v1.22.3/routers/api/v1/repo/issue_label.go (about) 1 // Copyright 2016 The Gogs Authors. All rights reserved. 2 // Copyright 2018 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package repo 6 7 import ( 8 "fmt" 9 "net/http" 10 "reflect" 11 12 issues_model "code.gitea.io/gitea/models/issues" 13 api "code.gitea.io/gitea/modules/structs" 14 "code.gitea.io/gitea/modules/web" 15 "code.gitea.io/gitea/services/context" 16 "code.gitea.io/gitea/services/convert" 17 issue_service "code.gitea.io/gitea/services/issue" 18 ) 19 20 // ListIssueLabels list all the labels of an issue 21 func ListIssueLabels(ctx *context.APIContext) { 22 // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/labels issue issueGetLabels 23 // --- 24 // summary: Get an issue's labels 25 // produces: 26 // - application/json 27 // parameters: 28 // - name: owner 29 // in: path 30 // description: owner of the repo 31 // type: string 32 // required: true 33 // - name: repo 34 // in: path 35 // description: name of the repo 36 // type: string 37 // required: true 38 // - name: index 39 // in: path 40 // description: index of the issue 41 // type: integer 42 // format: int64 43 // required: true 44 // responses: 45 // "200": 46 // "$ref": "#/responses/LabelList" 47 // "404": 48 // "$ref": "#/responses/notFound" 49 50 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 51 if err != nil { 52 if issues_model.IsErrIssueNotExist(err) { 53 ctx.NotFound() 54 } else { 55 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 56 } 57 return 58 } 59 60 if err := issue.LoadAttributes(ctx); err != nil { 61 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 62 return 63 } 64 65 ctx.JSON(http.StatusOK, convert.ToLabelList(issue.Labels, ctx.Repo.Repository, ctx.Repo.Owner)) 66 } 67 68 // AddIssueLabels add labels for an issue 69 func AddIssueLabels(ctx *context.APIContext) { 70 // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel 71 // --- 72 // summary: Add a label to an issue 73 // consumes: 74 // - application/json 75 // produces: 76 // - application/json 77 // parameters: 78 // - name: owner 79 // in: path 80 // description: owner of the repo 81 // type: string 82 // required: true 83 // - name: repo 84 // in: path 85 // description: name of the repo 86 // type: string 87 // required: true 88 // - name: index 89 // in: path 90 // description: index of the issue 91 // type: integer 92 // format: int64 93 // required: true 94 // - name: body 95 // in: body 96 // schema: 97 // "$ref": "#/definitions/IssueLabelsOption" 98 // responses: 99 // "200": 100 // "$ref": "#/responses/LabelList" 101 // "403": 102 // "$ref": "#/responses/forbidden" 103 // "404": 104 // "$ref": "#/responses/notFound" 105 106 form := web.GetForm(ctx).(*api.IssueLabelsOption) 107 issue, labels, err := prepareForReplaceOrAdd(ctx, *form) 108 if err != nil { 109 return 110 } 111 112 if err = issue_service.AddLabels(ctx, issue, ctx.Doer, labels); err != nil { 113 ctx.Error(http.StatusInternalServerError, "AddLabels", err) 114 return 115 } 116 117 labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID) 118 if err != nil { 119 ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err) 120 return 121 } 122 123 ctx.JSON(http.StatusOK, convert.ToLabelList(labels, ctx.Repo.Repository, ctx.Repo.Owner)) 124 } 125 126 // DeleteIssueLabel delete a label for an issue 127 func DeleteIssueLabel(ctx *context.APIContext) { 128 // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels/{id} issue issueRemoveLabel 129 // --- 130 // summary: Remove a label from an issue 131 // produces: 132 // - application/json 133 // parameters: 134 // - name: owner 135 // in: path 136 // description: owner of the repo 137 // type: string 138 // required: true 139 // - name: repo 140 // in: path 141 // description: name of the repo 142 // type: string 143 // required: true 144 // - name: index 145 // in: path 146 // description: index of the issue 147 // type: integer 148 // format: int64 149 // required: true 150 // - name: id 151 // in: path 152 // description: id of the label to remove 153 // type: integer 154 // format: int64 155 // required: true 156 // responses: 157 // "204": 158 // "$ref": "#/responses/empty" 159 // "403": 160 // "$ref": "#/responses/forbidden" 161 // "404": 162 // "$ref": "#/responses/notFound" 163 // "422": 164 // "$ref": "#/responses/validationError" 165 166 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 167 if err != nil { 168 if issues_model.IsErrIssueNotExist(err) { 169 ctx.NotFound() 170 } else { 171 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 172 } 173 return 174 } 175 176 if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { 177 ctx.Status(http.StatusForbidden) 178 return 179 } 180 181 label, err := issues_model.GetLabelByID(ctx, ctx.ParamsInt64(":id")) 182 if err != nil { 183 if issues_model.IsErrLabelNotExist(err) { 184 ctx.Error(http.StatusUnprocessableEntity, "", err) 185 } else { 186 ctx.Error(http.StatusInternalServerError, "GetLabelByID", err) 187 } 188 return 189 } 190 191 if err := issue_service.RemoveLabel(ctx, issue, ctx.Doer, label); err != nil { 192 ctx.Error(http.StatusInternalServerError, "DeleteIssueLabel", err) 193 return 194 } 195 196 ctx.Status(http.StatusNoContent) 197 } 198 199 // ReplaceIssueLabels replace labels for an issue 200 func ReplaceIssueLabels(ctx *context.APIContext) { 201 // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels 202 // --- 203 // summary: Replace an issue's labels 204 // consumes: 205 // - application/json 206 // produces: 207 // - application/json 208 // parameters: 209 // - name: owner 210 // in: path 211 // description: owner of the repo 212 // type: string 213 // required: true 214 // - name: repo 215 // in: path 216 // description: name of the repo 217 // type: string 218 // required: true 219 // - name: index 220 // in: path 221 // description: index of the issue 222 // type: integer 223 // format: int64 224 // required: true 225 // - name: body 226 // in: body 227 // schema: 228 // "$ref": "#/definitions/IssueLabelsOption" 229 // responses: 230 // "200": 231 // "$ref": "#/responses/LabelList" 232 // "403": 233 // "$ref": "#/responses/forbidden" 234 // "404": 235 // "$ref": "#/responses/notFound" 236 form := web.GetForm(ctx).(*api.IssueLabelsOption) 237 issue, labels, err := prepareForReplaceOrAdd(ctx, *form) 238 if err != nil { 239 return 240 } 241 242 if err := issue_service.ReplaceLabels(ctx, issue, ctx.Doer, labels); err != nil { 243 ctx.Error(http.StatusInternalServerError, "ReplaceLabels", err) 244 return 245 } 246 247 labels, err = issues_model.GetLabelsByIssueID(ctx, issue.ID) 248 if err != nil { 249 ctx.Error(http.StatusInternalServerError, "GetLabelsByIssueID", err) 250 return 251 } 252 253 ctx.JSON(http.StatusOK, convert.ToLabelList(labels, ctx.Repo.Repository, ctx.Repo.Owner)) 254 } 255 256 // ClearIssueLabels delete all the labels for an issue 257 func ClearIssueLabels(ctx *context.APIContext) { 258 // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/labels issue issueClearLabels 259 // --- 260 // summary: Remove all labels from an issue 261 // produces: 262 // - application/json 263 // parameters: 264 // - name: owner 265 // in: path 266 // description: owner of the repo 267 // type: string 268 // required: true 269 // - name: repo 270 // in: path 271 // description: name of the repo 272 // type: string 273 // required: true 274 // - name: index 275 // in: path 276 // description: index of the issue 277 // type: integer 278 // format: int64 279 // required: true 280 // responses: 281 // "204": 282 // "$ref": "#/responses/empty" 283 // "403": 284 // "$ref": "#/responses/forbidden" 285 // "404": 286 // "$ref": "#/responses/notFound" 287 288 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 289 if err != nil { 290 if issues_model.IsErrIssueNotExist(err) { 291 ctx.NotFound() 292 } else { 293 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 294 } 295 return 296 } 297 298 if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { 299 ctx.Status(http.StatusForbidden) 300 return 301 } 302 303 if err := issue_service.ClearLabels(ctx, issue, ctx.Doer); err != nil { 304 ctx.Error(http.StatusInternalServerError, "ClearLabels", err) 305 return 306 } 307 308 ctx.Status(http.StatusNoContent) 309 } 310 311 func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (*issues_model.Issue, []*issues_model.Label, error) { 312 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 313 if err != nil { 314 if issues_model.IsErrIssueNotExist(err) { 315 ctx.NotFound() 316 } else { 317 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 318 } 319 return nil, nil, err 320 } 321 322 var ( 323 labelIDs []int64 324 labelNames []string 325 ) 326 for _, label := range form.Labels { 327 rv := reflect.ValueOf(label) 328 switch rv.Kind() { 329 case reflect.Float64: 330 labelIDs = append(labelIDs, int64(rv.Float())) 331 case reflect.String: 332 labelNames = append(labelNames, rv.String()) 333 } 334 } 335 if len(labelIDs) > 0 && len(labelNames) > 0 { 336 ctx.Error(http.StatusBadRequest, "InvalidLabels", "labels should be an array of strings or integers") 337 return nil, nil, fmt.Errorf("invalid labels") 338 } 339 if len(labelNames) > 0 { 340 labelIDs, err = issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, labelNames) 341 if err != nil { 342 ctx.Error(http.StatusInternalServerError, "GetLabelIDsInRepoByNames", err) 343 return nil, nil, err 344 } 345 } 346 347 labels, err := issues_model.GetLabelsByIDs(ctx, labelIDs, "id", "repo_id", "org_id", "name", "exclusive") 348 if err != nil { 349 ctx.Error(http.StatusInternalServerError, "GetLabelsByIDs", err) 350 return nil, nil, err 351 } 352 353 if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { 354 ctx.Status(http.StatusForbidden) 355 return nil, nil, nil 356 } 357 358 return issue, labels, err 359 }