code.gitea.io/gitea@v1.22.3/routers/api/v1/repo/milestone.go (about) 1 // Copyright 2016 The Gogs Authors. All rights reserved. 2 // Copyright 2020 The Gitea Authors. 3 // SPDX-License-Identifier: MIT 4 5 package repo 6 7 import ( 8 "net/http" 9 "strconv" 10 "time" 11 12 "code.gitea.io/gitea/models/db" 13 issues_model "code.gitea.io/gitea/models/issues" 14 "code.gitea.io/gitea/modules/optional" 15 api "code.gitea.io/gitea/modules/structs" 16 "code.gitea.io/gitea/modules/timeutil" 17 "code.gitea.io/gitea/modules/web" 18 "code.gitea.io/gitea/routers/api/v1/utils" 19 "code.gitea.io/gitea/services/context" 20 "code.gitea.io/gitea/services/convert" 21 ) 22 23 // ListMilestones list milestones for a repository 24 func ListMilestones(ctx *context.APIContext) { 25 // swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestonesList 26 // --- 27 // summary: Get all of a repository's opened milestones 28 // produces: 29 // - application/json 30 // parameters: 31 // - name: owner 32 // in: path 33 // description: owner of the repo 34 // type: string 35 // required: true 36 // - name: repo 37 // in: path 38 // description: name of the repo 39 // type: string 40 // required: true 41 // - name: state 42 // in: query 43 // description: Milestone state, Recognized values are open, closed and all. Defaults to "open" 44 // type: string 45 // - name: name 46 // in: query 47 // description: filter by milestone name 48 // type: string 49 // - name: page 50 // in: query 51 // description: page number of results to return (1-based) 52 // type: integer 53 // - name: limit 54 // in: query 55 // description: page size of results 56 // type: integer 57 // responses: 58 // "200": 59 // "$ref": "#/responses/MilestoneList" 60 // "404": 61 // "$ref": "#/responses/notFound" 62 63 state := api.StateType(ctx.FormString("state")) 64 var isClosed optional.Option[bool] 65 switch state { 66 case api.StateClosed, api.StateOpen: 67 isClosed = optional.Some(state == api.StateClosed) 68 } 69 70 milestones, total, err := db.FindAndCount[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ 71 ListOptions: utils.GetListOptions(ctx), 72 RepoID: ctx.Repo.Repository.ID, 73 IsClosed: isClosed, 74 Name: ctx.FormString("name"), 75 }) 76 if err != nil { 77 ctx.Error(http.StatusInternalServerError, "db.FindAndCount[issues_model.Milestone]", err) 78 return 79 } 80 81 apiMilestones := make([]*api.Milestone, len(milestones)) 82 for i := range milestones { 83 apiMilestones[i] = convert.ToAPIMilestone(milestones[i]) 84 } 85 86 ctx.SetTotalCountHeader(total) 87 ctx.JSON(http.StatusOK, &apiMilestones) 88 } 89 90 // GetMilestone get a milestone for a repository by ID and if not available by name 91 func GetMilestone(ctx *context.APIContext) { 92 // swagger:operation GET /repos/{owner}/{repo}/milestones/{id} issue issueGetMilestone 93 // --- 94 // summary: Get a milestone 95 // produces: 96 // - application/json 97 // parameters: 98 // - name: owner 99 // in: path 100 // description: owner of the repo 101 // type: string 102 // required: true 103 // - name: repo 104 // in: path 105 // description: name of the repo 106 // type: string 107 // required: true 108 // - name: id 109 // in: path 110 // description: the milestone to get, identified by ID and if not available by name 111 // type: string 112 // required: true 113 // responses: 114 // "200": 115 // "$ref": "#/responses/Milestone" 116 // "404": 117 // "$ref": "#/responses/notFound" 118 119 milestone := getMilestoneByIDOrName(ctx) 120 if ctx.Written() { 121 return 122 } 123 124 ctx.JSON(http.StatusOK, convert.ToAPIMilestone(milestone)) 125 } 126 127 // CreateMilestone create a milestone for a repository 128 func CreateMilestone(ctx *context.APIContext) { 129 // swagger:operation POST /repos/{owner}/{repo}/milestones issue issueCreateMilestone 130 // --- 131 // summary: Create a milestone 132 // consumes: 133 // - application/json 134 // produces: 135 // - application/json 136 // parameters: 137 // - name: owner 138 // in: path 139 // description: owner of the repo 140 // type: string 141 // required: true 142 // - name: repo 143 // in: path 144 // description: name of the repo 145 // type: string 146 // required: true 147 // - name: body 148 // in: body 149 // schema: 150 // "$ref": "#/definitions/CreateMilestoneOption" 151 // responses: 152 // "201": 153 // "$ref": "#/responses/Milestone" 154 // "404": 155 // "$ref": "#/responses/notFound" 156 form := web.GetForm(ctx).(*api.CreateMilestoneOption) 157 158 if form.Deadline == nil { 159 defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local) 160 form.Deadline = &defaultDeadline 161 } 162 163 milestone := &issues_model.Milestone{ 164 RepoID: ctx.Repo.Repository.ID, 165 Name: form.Title, 166 Content: form.Description, 167 DeadlineUnix: timeutil.TimeStamp(form.Deadline.Unix()), 168 } 169 170 if form.State == "closed" { 171 milestone.IsClosed = true 172 milestone.ClosedDateUnix = timeutil.TimeStampNow() 173 } 174 175 if err := issues_model.NewMilestone(ctx, milestone); err != nil { 176 ctx.Error(http.StatusInternalServerError, "NewMilestone", err) 177 return 178 } 179 ctx.JSON(http.StatusCreated, convert.ToAPIMilestone(milestone)) 180 } 181 182 // EditMilestone modify a milestone for a repository by ID and if not available by name 183 func EditMilestone(ctx *context.APIContext) { 184 // swagger:operation PATCH /repos/{owner}/{repo}/milestones/{id} issue issueEditMilestone 185 // --- 186 // summary: Update a milestone 187 // consumes: 188 // - application/json 189 // produces: 190 // - application/json 191 // parameters: 192 // - name: owner 193 // in: path 194 // description: owner of the repo 195 // type: string 196 // required: true 197 // - name: repo 198 // in: path 199 // description: name of the repo 200 // type: string 201 // required: true 202 // - name: id 203 // in: path 204 // description: the milestone to edit, identified by ID and if not available by name 205 // type: string 206 // required: true 207 // - name: body 208 // in: body 209 // schema: 210 // "$ref": "#/definitions/EditMilestoneOption" 211 // responses: 212 // "200": 213 // "$ref": "#/responses/Milestone" 214 // "404": 215 // "$ref": "#/responses/notFound" 216 form := web.GetForm(ctx).(*api.EditMilestoneOption) 217 milestone := getMilestoneByIDOrName(ctx) 218 if ctx.Written() { 219 return 220 } 221 222 if len(form.Title) > 0 { 223 milestone.Name = form.Title 224 } 225 if form.Description != nil { 226 milestone.Content = *form.Description 227 } 228 if form.Deadline != nil && !form.Deadline.IsZero() { 229 milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix()) 230 } 231 232 oldIsClosed := milestone.IsClosed 233 if form.State != nil { 234 milestone.IsClosed = *form.State == string(api.StateClosed) 235 } 236 237 if err := issues_model.UpdateMilestone(ctx, milestone, oldIsClosed); err != nil { 238 ctx.Error(http.StatusInternalServerError, "UpdateMilestone", err) 239 return 240 } 241 ctx.JSON(http.StatusOK, convert.ToAPIMilestone(milestone)) 242 } 243 244 // DeleteMilestone delete a milestone for a repository by ID and if not available by name 245 func DeleteMilestone(ctx *context.APIContext) { 246 // swagger:operation DELETE /repos/{owner}/{repo}/milestones/{id} issue issueDeleteMilestone 247 // --- 248 // summary: Delete a milestone 249 // parameters: 250 // - name: owner 251 // in: path 252 // description: owner of the repo 253 // type: string 254 // required: true 255 // - name: repo 256 // in: path 257 // description: name of the repo 258 // type: string 259 // required: true 260 // - name: id 261 // in: path 262 // description: the milestone to delete, identified by ID and if not available by name 263 // type: string 264 // required: true 265 // responses: 266 // "204": 267 // "$ref": "#/responses/empty" 268 // "404": 269 // "$ref": "#/responses/notFound" 270 271 m := getMilestoneByIDOrName(ctx) 272 if ctx.Written() { 273 return 274 } 275 276 if err := issues_model.DeleteMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, m.ID); err != nil { 277 ctx.Error(http.StatusInternalServerError, "DeleteMilestoneByRepoID", err) 278 return 279 } 280 ctx.Status(http.StatusNoContent) 281 } 282 283 // getMilestoneByIDOrName get milestone by ID and if not available by name 284 func getMilestoneByIDOrName(ctx *context.APIContext) *issues_model.Milestone { 285 mile := ctx.Params(":id") 286 mileID, _ := strconv.ParseInt(mile, 0, 64) 287 288 if mileID != 0 { 289 milestone, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, mileID) 290 if err == nil { 291 return milestone 292 } else if !issues_model.IsErrMilestoneNotExist(err) { 293 ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err) 294 return nil 295 } 296 } 297 298 milestone, err := issues_model.GetMilestoneByRepoIDANDName(ctx, ctx.Repo.Repository.ID, mile) 299 if err != nil { 300 if issues_model.IsErrMilestoneNotExist(err) { 301 ctx.NotFound() 302 return nil 303 } 304 ctx.Error(http.StatusInternalServerError, "GetMilestoneByRepoID", err) 305 return nil 306 } 307 308 return milestone 309 }