code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/issue_stopwatch.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "errors" 8 "net/http" 9 10 issues_model "code.gitea.io/gitea/models/issues" 11 "code.gitea.io/gitea/modules/context" 12 "code.gitea.io/gitea/routers/api/v1/utils" 13 "code.gitea.io/gitea/services/convert" 14 ) 15 16 // StartIssueStopwatch creates a stopwatch for the given issue. 17 func StartIssueStopwatch(ctx *context.APIContext) { 18 // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch 19 // --- 20 // summary: Start stopwatch on an issue. 21 // consumes: 22 // - application/json 23 // produces: 24 // - application/json 25 // parameters: 26 // - name: owner 27 // in: path 28 // description: owner of the repo 29 // type: string 30 // required: true 31 // - name: repo 32 // in: path 33 // description: name of the repo 34 // type: string 35 // required: true 36 // - name: index 37 // in: path 38 // description: index of the issue to create the stopwatch on 39 // type: integer 40 // format: int64 41 // required: true 42 // responses: 43 // "201": 44 // "$ref": "#/responses/empty" 45 // "403": 46 // description: Not repo writer, user does not have rights to toggle stopwatch 47 // "404": 48 // "$ref": "#/responses/notFound" 49 // "409": 50 // description: Cannot start a stopwatch again if it already exists 51 52 issue, err := prepareIssueStopwatch(ctx, false) 53 if err != nil { 54 return 55 } 56 57 if err := issues_model.CreateIssueStopwatch(ctx, ctx.Doer, issue); err != nil { 58 ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err) 59 return 60 } 61 62 ctx.Status(http.StatusCreated) 63 } 64 65 // StopIssueStopwatch stops a stopwatch for the given issue. 66 func StopIssueStopwatch(ctx *context.APIContext) { 67 // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopStopWatch 68 // --- 69 // summary: Stop an issue's existing stopwatch. 70 // consumes: 71 // - application/json 72 // produces: 73 // - application/json 74 // parameters: 75 // - name: owner 76 // in: path 77 // description: owner of the repo 78 // type: string 79 // required: true 80 // - name: repo 81 // in: path 82 // description: name of the repo 83 // type: string 84 // required: true 85 // - name: index 86 // in: path 87 // description: index of the issue to stop the stopwatch on 88 // type: integer 89 // format: int64 90 // required: true 91 // responses: 92 // "201": 93 // "$ref": "#/responses/empty" 94 // "403": 95 // description: Not repo writer, user does not have rights to toggle stopwatch 96 // "404": 97 // "$ref": "#/responses/notFound" 98 // "409": 99 // description: Cannot stop a non existent stopwatch 100 101 issue, err := prepareIssueStopwatch(ctx, true) 102 if err != nil { 103 return 104 } 105 106 if err := issues_model.FinishIssueStopwatch(ctx, ctx.Doer, issue); err != nil { 107 ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err) 108 return 109 } 110 111 ctx.Status(http.StatusCreated) 112 } 113 114 // DeleteIssueStopwatch delete a specific stopwatch 115 func DeleteIssueStopwatch(ctx *context.APIContext) { 116 // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/stopwatch/delete issue issueDeleteStopWatch 117 // --- 118 // summary: Delete an issue's existing stopwatch. 119 // consumes: 120 // - application/json 121 // produces: 122 // - application/json 123 // parameters: 124 // - name: owner 125 // in: path 126 // description: owner of the repo 127 // type: string 128 // required: true 129 // - name: repo 130 // in: path 131 // description: name of the repo 132 // type: string 133 // required: true 134 // - name: index 135 // in: path 136 // description: index of the issue to stop the stopwatch on 137 // type: integer 138 // format: int64 139 // required: true 140 // responses: 141 // "204": 142 // "$ref": "#/responses/empty" 143 // "403": 144 // description: Not repo writer, user does not have rights to toggle stopwatch 145 // "404": 146 // "$ref": "#/responses/notFound" 147 // "409": 148 // description: Cannot cancel a non existent stopwatch 149 150 issue, err := prepareIssueStopwatch(ctx, true) 151 if err != nil { 152 return 153 } 154 155 if err := issues_model.CancelStopwatch(ctx, ctx.Doer, issue); err != nil { 156 ctx.Error(http.StatusInternalServerError, "CancelStopwatch", err) 157 return 158 } 159 160 ctx.Status(http.StatusNoContent) 161 } 162 163 func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*issues_model.Issue, error) { 164 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 165 if err != nil { 166 if issues_model.IsErrIssueNotExist(err) { 167 ctx.NotFound() 168 } else { 169 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 170 } 171 172 return nil, err 173 } 174 175 if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { 176 ctx.Status(http.StatusForbidden) 177 return nil, errors.New("Unable to write to PRs") 178 } 179 180 if !ctx.Repo.CanUseTimetracker(issue, ctx.Doer) { 181 ctx.Status(http.StatusForbidden) 182 return nil, errors.New("Cannot use time tracker") 183 } 184 185 if issues_model.StopwatchExists(ctx, ctx.Doer.ID, issue.ID) != shouldExist { 186 if shouldExist { 187 ctx.Error(http.StatusConflict, "StopwatchExists", "cannot stop/cancel a non existent stopwatch") 188 err = errors.New("cannot stop/cancel a non existent stopwatch") 189 } else { 190 ctx.Error(http.StatusConflict, "StopwatchExists", "cannot start a stopwatch again if it already exists") 191 err = errors.New("cannot start a stopwatch again if it already exists") 192 } 193 return nil, err 194 } 195 196 return issue, nil 197 } 198 199 // GetStopwatches get all stopwatches 200 func GetStopwatches(ctx *context.APIContext) { 201 // swagger:operation GET /user/stopwatches user userGetStopWatches 202 // --- 203 // summary: Get list of all existing stopwatches 204 // parameters: 205 // - name: page 206 // in: query 207 // description: page number of results to return (1-based) 208 // type: integer 209 // - name: limit 210 // in: query 211 // description: page size of results 212 // type: integer 213 // consumes: 214 // - application/json 215 // produces: 216 // - application/json 217 // responses: 218 // "200": 219 // "$ref": "#/responses/StopWatchList" 220 221 sws, err := issues_model.GetUserStopwatches(ctx, ctx.Doer.ID, utils.GetListOptions(ctx)) 222 if err != nil { 223 ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err) 224 return 225 } 226 227 count, err := issues_model.CountUserStopwatches(ctx, ctx.Doer.ID) 228 if err != nil { 229 ctx.InternalServerError(err) 230 return 231 } 232 233 apiSWs, err := convert.ToStopWatches(sws) 234 if err != nil { 235 ctx.Error(http.StatusInternalServerError, "APIFormat", err) 236 return 237 } 238 239 ctx.SetTotalCountHeader(count) 240 ctx.JSON(http.StatusOK, apiSWs) 241 }