[API] extend StopWatch (#9196)
* squash api-stopwatch * fix prepair logic! + add Tests * fix lint * more robust time compare * delete responce 202 -> 204 * change http responce in test too
This commit is contained in:
		
							parent
							
								
									382936a668
								
							
						
					
					
						commit
						aceb1085c7
					
				
							
								
								
									
										83
									
								
								integrations/api_issue_stopwatch_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								integrations/api_issue_stopwatch_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package integrations | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestAPIListStopWatches(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 
 | ||||
| 	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) | ||||
| 	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) | ||||
| 
 | ||||
| 	session := loginUser(t, owner.Name) | ||||
| 	token := getTokenForLoggedInUser(t, session) | ||||
| 	req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| 	var apiWatches []*api.StopWatch | ||||
| 	DecodeJSON(t, resp, &apiWatches) | ||||
| 	expect := models.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch) | ||||
| 	expectAPI, _ := expect.APIFormat() | ||||
| 	assert.Len(t, apiWatches, 1) | ||||
| 
 | ||||
| 	assert.EqualValues(t, expectAPI.IssueIndex, apiWatches[0].IssueIndex) | ||||
| 	assert.EqualValues(t, expectAPI.Created.Unix(), apiWatches[0].Created.Unix()) | ||||
| } | ||||
| 
 | ||||
| func TestAPIStopStopWatches(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 
 | ||||
| 	issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | ||||
| 	_ = issue.LoadRepo() | ||||
| 	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User) | ||||
| 	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) | ||||
| 
 | ||||
| 	session := loginUser(t, user.Name) | ||||
| 	token := getTokenForLoggedInUser(t, session) | ||||
| 
 | ||||
| 	req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | ||||
| 	session.MakeRequest(t, req, http.StatusCreated) | ||||
| 	session.MakeRequest(t, req, http.StatusConflict) | ||||
| } | ||||
| 
 | ||||
| func TestAPICancelStopWatches(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 
 | ||||
| 	issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) | ||||
| 	_ = issue.LoadRepo() | ||||
| 	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User) | ||||
| 	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) | ||||
| 
 | ||||
| 	session := loginUser(t, user.Name) | ||||
| 	token := getTokenForLoggedInUser(t, session) | ||||
| 
 | ||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | ||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | ||||
| 	session.MakeRequest(t, req, http.StatusConflict) | ||||
| } | ||||
| 
 | ||||
| func TestAPIStartStopWatches(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
| 
 | ||||
| 	issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) | ||||
| 	_ = issue.LoadRepo() | ||||
| 	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User) | ||||
| 	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) | ||||
| 
 | ||||
| 	session := loginUser(t, user.Name) | ||||
| 	token := getTokenForLoggedInUser(t, session) | ||||
| 
 | ||||
| 	req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) | ||||
| 	session.MakeRequest(t, req, http.StatusCreated) | ||||
| 	session.MakeRequest(t, req, http.StatusConflict) | ||||
| } | ||||
| @ -2,10 +2,10 @@ | ||||
|   id: 1 | ||||
|   user_id: 1 | ||||
|   issue_id: 1 | ||||
|   created_unix: 1500988502 | ||||
|   created_unix: 1500988001 | ||||
| 
 | ||||
| - | ||||
|   id: 2 | ||||
|   user_id: 2 | ||||
|   issue_id: 2 | ||||
|   created_unix: 1500988502 | ||||
|   created_unix: 1500988002 | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| ) | ||||
| 
 | ||||
| @ -19,6 +20,9 @@ type Stopwatch struct { | ||||
| 	CreatedUnix timeutil.TimeStamp `xorm:"created"` | ||||
| } | ||||
| 
 | ||||
| // Stopwatches is a List ful of Stopwatch
 | ||||
| type Stopwatches []Stopwatch | ||||
| 
 | ||||
| func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) { | ||||
| 	sw = new(Stopwatch) | ||||
| 	exists, err = e. | ||||
| @ -28,6 +32,16 @@ func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // GetUserStopwatches return list of all stopwatches of a user
 | ||||
| func GetUserStopwatches(userID int64) (sws *Stopwatches, err error) { | ||||
| 	sws = new(Stopwatches) | ||||
| 	err = x.Where("stopwatch.user_id = ?", userID).Find(sws) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return sws, nil | ||||
| } | ||||
| 
 | ||||
| // StopwatchExists returns true if the stopwatch exists
 | ||||
| func StopwatchExists(userID int64, issueID int64) bool { | ||||
| 	_, exists, _ := getStopwatch(x, userID, issueID) | ||||
| @ -160,3 +174,28 @@ func SecToTime(duration int64) string { | ||||
| 
 | ||||
| 	return hrs | ||||
| } | ||||
| 
 | ||||
| // APIFormat convert Stopwatch type to api.StopWatch type
 | ||||
| func (sw *Stopwatch) APIFormat() (api.StopWatch, error) { | ||||
| 	issue, err := getIssueByID(x, sw.IssueID) | ||||
| 	if err != nil { | ||||
| 		return api.StopWatch{}, err | ||||
| 	} | ||||
| 	return api.StopWatch{ | ||||
| 		Created:    sw.CreatedUnix.AsTime(), | ||||
| 		IssueIndex: issue.Index, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // APIFormat convert Stopwatches type to api.StopWatches type
 | ||||
| func (sws Stopwatches) APIFormat() (api.StopWatches, error) { | ||||
| 	result := api.StopWatches(make([]api.StopWatch, 0, len(sws))) | ||||
| 	for _, sw := range sws { | ||||
| 		apiSW, err := sw.APIFormat() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		result = append(result, apiSW) | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										19
									
								
								modules/structs/issue_stopwatch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								modules/structs/issue_stopwatch.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package structs | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // StopWatch represent a running stopwatch
 | ||||
| type StopWatch struct { | ||||
| 	// swagger:strfmt date-time
 | ||||
| 	Created    time.Time `json:"created"` | ||||
| 	IssueIndex int64     `json:"issue_index"` | ||||
| } | ||||
| 
 | ||||
| // StopWatches represent a list of stopwatches
 | ||||
| type StopWatches []StopWatch | ||||
| @ -584,6 +584,8 @@ func RegisterRoutes(m *macaron.Macaron) { | ||||
| 			}) | ||||
| 			m.Get("/times", repo.ListMyTrackedTimes) | ||||
| 
 | ||||
| 			m.Get("/stopwatches", repo.GetStopwatches) | ||||
| 
 | ||||
| 			m.Get("/subscriptions", user.GetMyWatchedRepos) | ||||
| 
 | ||||
| 			m.Get("/teams", org.ListUserTeams) | ||||
| @ -691,6 +693,7 @@ func RegisterRoutes(m *macaron.Macaron) { | ||||
| 						m.Group("/stopwatch", func() { | ||||
| 							m.Post("/start", reqToken(), repo.StartIssueStopwatch) | ||||
| 							m.Post("/stop", reqToken(), repo.StopIssueStopwatch) | ||||
| 							m.Delete("/delete", reqToken(), repo.DeleteIssueStopwatch) | ||||
| 						}) | ||||
| 						m.Group("/subscriptions", func() { | ||||
| 							m.Get("", repo.GetIssueSubscribers) | ||||
|  | ||||
| @ -598,141 +598,3 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { | ||||
| 
 | ||||
| 	ctx.JSON(201, api.IssueDeadline{Deadline: &deadline}) | ||||
| } | ||||
| 
 | ||||
| // StartIssueStopwatch creates a stopwatch for the given issue.
 | ||||
| func StartIssueStopwatch(ctx *context.APIContext) { | ||||
| 	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch
 | ||||
| 	// ---
 | ||||
| 	// summary: Start stopwatch on an issue.
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// parameters:
 | ||||
| 	// - name: owner
 | ||||
| 	//   in: path
 | ||||
| 	//   description: owner of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: repo
 | ||||
| 	//   in: path
 | ||||
| 	//   description: name of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: index
 | ||||
| 	//   in: path
 | ||||
| 	//   description: index of the issue to create the stopwatch on
 | ||||
| 	//   type: integer
 | ||||
| 	//   format: int64
 | ||||
| 	//   required: true
 | ||||
| 	// responses:
 | ||||
| 	//   "201":
 | ||||
| 	//     "$ref": "#/responses/empty"
 | ||||
| 	//   "403":
 | ||||
| 	//     description: Not repo writer, user does not have rights to toggle stopwatch
 | ||||
| 	//   "404":
 | ||||
| 	//     description: Issue not found
 | ||||
| 	//   "409":
 | ||||
| 	//     description: Cannot start a stopwatch again if it already exists
 | ||||
| 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | ||||
| 	if err != nil { | ||||
| 		if models.IsErrIssueNotExist(err) { | ||||
| 			ctx.NotFound() | ||||
| 		} else { | ||||
| 			ctx.Error(500, "GetIssueByIndex", err) | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanWrite(models.UnitTypeIssues) { | ||||
| 		ctx.Status(403) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanUseTimetracker(issue, ctx.User) { | ||||
| 		ctx.Status(403) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if models.StopwatchExists(ctx.User.ID, issue.ID) { | ||||
| 		ctx.Error(409, "StopwatchExists", "a stopwatch has already been started for this issue") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil { | ||||
| 		ctx.Error(500, "CreateOrStopIssueStopwatch", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(201) | ||||
| } | ||||
| 
 | ||||
| // StopIssueStopwatch stops a stopwatch for the given issue.
 | ||||
| func StopIssueStopwatch(ctx *context.APIContext) { | ||||
| 	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopWatch
 | ||||
| 	// ---
 | ||||
| 	// summary: Stop an issue's existing stopwatch.
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// parameters:
 | ||||
| 	// - name: owner
 | ||||
| 	//   in: path
 | ||||
| 	//   description: owner of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: repo
 | ||||
| 	//   in: path
 | ||||
| 	//   description: name of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: index
 | ||||
| 	//   in: path
 | ||||
| 	//   description: index of the issue to stop the stopwatch on
 | ||||
| 	//   type: integer
 | ||||
| 	//   format: int64
 | ||||
| 	//   required: true
 | ||||
| 	// responses:
 | ||||
| 	//   "201":
 | ||||
| 	//     "$ref": "#/responses/empty"
 | ||||
| 	//   "403":
 | ||||
| 	//     description: Not repo writer, user does not have rights to toggle stopwatch
 | ||||
| 	//   "404":
 | ||||
| 	//     description: Issue not found
 | ||||
| 	//   "409":
 | ||||
| 	//     description:  Cannot stop a non existent stopwatch
 | ||||
| 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | ||||
| 	if err != nil { | ||||
| 		if models.IsErrIssueNotExist(err) { | ||||
| 			ctx.NotFound() | ||||
| 		} else { | ||||
| 			ctx.Error(500, "GetIssueByIndex", err) | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanWrite(models.UnitTypeIssues) { | ||||
| 		ctx.Status(403) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanUseTimetracker(issue, ctx.User) { | ||||
| 		ctx.Status(403) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !models.StopwatchExists(ctx.User.ID, issue.ID) { | ||||
| 		ctx.Error(409, "StopwatchExists", "cannot stop a non existent stopwatch") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil { | ||||
| 		ctx.Error(500, "CreateOrStopIssueStopwatch", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(201) | ||||
| } | ||||
|  | ||||
							
								
								
									
										216
									
								
								routers/api/v1/repo/issue_stopwatch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								routers/api/v1/repo/issue_stopwatch.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,216 @@ | ||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package repo | ||||
| 
 | ||||
| import ( | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| ) | ||||
| 
 | ||||
| // StartIssueStopwatch creates a stopwatch for the given issue.
 | ||||
| func StartIssueStopwatch(ctx *context.APIContext) { | ||||
| 	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch
 | ||||
| 	// ---
 | ||||
| 	// summary: Start stopwatch on an issue.
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// parameters:
 | ||||
| 	// - name: owner
 | ||||
| 	//   in: path
 | ||||
| 	//   description: owner of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: repo
 | ||||
| 	//   in: path
 | ||||
| 	//   description: name of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: index
 | ||||
| 	//   in: path
 | ||||
| 	//   description: index of the issue to create the stopwatch on
 | ||||
| 	//   type: integer
 | ||||
| 	//   format: int64
 | ||||
| 	//   required: true
 | ||||
| 	// responses:
 | ||||
| 	//   "201":
 | ||||
| 	//     "$ref": "#/responses/empty"
 | ||||
| 	//   "403":
 | ||||
| 	//     description: Not repo writer, user does not have rights to toggle stopwatch
 | ||||
| 	//   "404":
 | ||||
| 	//     description: Issue not found
 | ||||
| 	//   "409":
 | ||||
| 	//     description: Cannot start a stopwatch again if it already exists
 | ||||
| 	issue, err := prepareIssueStopwatch(ctx, false) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil { | ||||
| 		ctx.Error(500, "CreateOrStopIssueStopwatch", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(201) | ||||
| } | ||||
| 
 | ||||
| // StopIssueStopwatch stops a stopwatch for the given issue.
 | ||||
| func StopIssueStopwatch(ctx *context.APIContext) { | ||||
| 	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopStopWatch
 | ||||
| 	// ---
 | ||||
| 	// summary: Stop an issue's existing stopwatch.
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// parameters:
 | ||||
| 	// - name: owner
 | ||||
| 	//   in: path
 | ||||
| 	//   description: owner of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: repo
 | ||||
| 	//   in: path
 | ||||
| 	//   description: name of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: index
 | ||||
| 	//   in: path
 | ||||
| 	//   description: index of the issue to stop the stopwatch on
 | ||||
| 	//   type: integer
 | ||||
| 	//   format: int64
 | ||||
| 	//   required: true
 | ||||
| 	// responses:
 | ||||
| 	//   "201":
 | ||||
| 	//     "$ref": "#/responses/empty"
 | ||||
| 	//   "403":
 | ||||
| 	//     description: Not repo writer, user does not have rights to toggle stopwatch
 | ||||
| 	//   "404":
 | ||||
| 	//     description: Issue not found
 | ||||
| 	//   "409":
 | ||||
| 	//     description:  Cannot stop a non existent stopwatch
 | ||||
| 	issue, err := prepareIssueStopwatch(ctx, true) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil { | ||||
| 		ctx.Error(500, "CreateOrStopIssueStopwatch", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(201) | ||||
| } | ||||
| 
 | ||||
| // DeleteIssueStopwatch delete a specific stopwatch
 | ||||
| func DeleteIssueStopwatch(ctx *context.APIContext) { | ||||
| 	// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/stopwatch/delete issue issueDeleteStopWatch
 | ||||
| 	// ---
 | ||||
| 	// summary: Delete an issue's existing stopwatch.
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// parameters:
 | ||||
| 	// - name: owner
 | ||||
| 	//   in: path
 | ||||
| 	//   description: owner of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: repo
 | ||||
| 	//   in: path
 | ||||
| 	//   description: name of the repo
 | ||||
| 	//   type: string
 | ||||
| 	//   required: true
 | ||||
| 	// - name: index
 | ||||
| 	//   in: path
 | ||||
| 	//   description: index of the issue to stop the stopwatch on
 | ||||
| 	//   type: integer
 | ||||
| 	//   format: int64
 | ||||
| 	//   required: true
 | ||||
| 	// responses:
 | ||||
| 	//   "204":
 | ||||
| 	//     "$ref": "#/responses/empty"
 | ||||
| 	//   "403":
 | ||||
| 	//     description: Not repo writer, user does not have rights to toggle stopwatch
 | ||||
| 	//   "404":
 | ||||
| 	//     description: Issue not found
 | ||||
| 	//   "409":
 | ||||
| 	//     description:  Cannot cancel a non existent stopwatch
 | ||||
| 	issue, err := prepareIssueStopwatch(ctx, true) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := models.CancelStopwatch(ctx.User, issue); err != nil { | ||||
| 		ctx.Error(500, "CancelStopwatch", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Status(204) | ||||
| } | ||||
| 
 | ||||
| func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*models.Issue, error) { | ||||
| 	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | ||||
| 	if err != nil { | ||||
| 		if models.IsErrIssueNotExist(err) { | ||||
| 			ctx.NotFound() | ||||
| 		} else { | ||||
| 			ctx.Error(500, "GetIssueByIndex", err) | ||||
| 		} | ||||
| 
 | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanWrite(models.UnitTypeIssues) { | ||||
| 		ctx.Status(403) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.Repo.CanUseTimetracker(issue, ctx.User) { | ||||
| 		ctx.Status(403) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if models.StopwatchExists(ctx.User.ID, issue.ID) != shouldExist { | ||||
| 		if shouldExist { | ||||
| 			ctx.Error(409, "StopwatchExists", "cannot stop/cancel a non existent stopwatch") | ||||
| 		} else { | ||||
| 			ctx.Error(409, "StopwatchExists", "cannot start a stopwatch again if it already exists") | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return issue, nil | ||||
| } | ||||
| 
 | ||||
| // GetStopwatches get all stopwatches
 | ||||
| func GetStopwatches(ctx *context.APIContext) { | ||||
| 	// swagger:operation GET /user/stopwatches user userGetStopWatches
 | ||||
| 	// ---
 | ||||
| 	// summary: Get list of all existing stopwatches
 | ||||
| 	// consumes:
 | ||||
| 	// - application/json
 | ||||
| 	// produces:
 | ||||
| 	// - application/json
 | ||||
| 	// responses:
 | ||||
| 	//   "200":
 | ||||
| 	//     "$ref": "#/responses/StopWatchList"
 | ||||
| 
 | ||||
| 	sws, err := models.GetUserStopwatches(ctx.User.ID) | ||||
| 	if err != nil { | ||||
| 		ctx.Error(500, "GetUserStopwatches", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	apiSWs, err := sws.APIFormat() | ||||
| 	if err != nil { | ||||
| 		ctx.Error(500, "APIFormat", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.JSON(200, apiSWs) | ||||
| } | ||||
| @ -85,6 +85,20 @@ type swaggerIssueDeadline struct { | ||||
| 	Body api.IssueDeadline `json:"body"` | ||||
| } | ||||
| 
 | ||||
| // StopWatch
 | ||||
| // swagger:response StopWatch
 | ||||
| type swaggerResponseStopWatch struct { | ||||
| 	// in:body
 | ||||
| 	Body api.StopWatch `json:"body"` | ||||
| } | ||||
| 
 | ||||
| // StopWatchList
 | ||||
| // swagger:response StopWatchList
 | ||||
| type swaggerResponseStopWatchList struct { | ||||
| 	// in:body
 | ||||
| 	Body []api.StopWatch `json:"body"` | ||||
| } | ||||
| 
 | ||||
| // EditReactionOption
 | ||||
| // swagger:response EditReactionOption
 | ||||
| type swaggerEditReactionOption struct { | ||||
|  | ||||
| @ -3972,6 +3972,59 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/repos/{owner}/{repo}/issues/{index}/stopwatch/delete": { | ||||
|       "delete": { | ||||
|         "consumes": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "produces": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "issue" | ||||
|         ], | ||||
|         "summary": "Delete an issue's existing stopwatch.", | ||||
|         "operationId": "issueDeleteStopWatch", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "type": "string", | ||||
|             "description": "owner of the repo", | ||||
|             "name": "owner", | ||||
|             "in": "path", | ||||
|             "required": true | ||||
|           }, | ||||
|           { | ||||
|             "type": "string", | ||||
|             "description": "name of the repo", | ||||
|             "name": "repo", | ||||
|             "in": "path", | ||||
|             "required": true | ||||
|           }, | ||||
|           { | ||||
|             "type": "integer", | ||||
|             "format": "int64", | ||||
|             "description": "index of the issue to stop the stopwatch on", | ||||
|             "name": "index", | ||||
|             "in": "path", | ||||
|             "required": true | ||||
|           } | ||||
|         ], | ||||
|         "responses": { | ||||
|           "204": { | ||||
|             "$ref": "#/responses/empty" | ||||
|           }, | ||||
|           "403": { | ||||
|             "description": "Not repo writer, user does not have rights to toggle stopwatch" | ||||
|           }, | ||||
|           "404": { | ||||
|             "description": "Issue not found" | ||||
|           }, | ||||
|           "409": { | ||||
|             "description": "Cannot cancel a non existent stopwatch" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/repos/{owner}/{repo}/issues/{index}/stopwatch/start": { | ||||
|       "post": { | ||||
|         "consumes": [ | ||||
| @ -4037,7 +4090,7 @@ | ||||
|           "issue" | ||||
|         ], | ||||
|         "summary": "Stop an issue's existing stopwatch.", | ||||
|         "operationId": "issueStopWatch", | ||||
|         "operationId": "issueStopStopWatch", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "type": "string", | ||||
| @ -7174,6 +7227,26 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/user/stopwatches": { | ||||
|       "get": { | ||||
|         "consumes": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "produces": [ | ||||
|           "application/json" | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "user" | ||||
|         ], | ||||
|         "summary": "Get list of all existing stopwatches", | ||||
|         "operationId": "userGetStopWatches", | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "$ref": "#/responses/StopWatchList" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "/user/subscriptions": { | ||||
|       "get": { | ||||
|         "produces": [ | ||||
| @ -10808,6 +10881,23 @@ | ||||
|       "type": "string", | ||||
|       "x-go-package": "code.gitea.io/gitea/modules/structs" | ||||
|     }, | ||||
|     "StopWatch": { | ||||
|       "description": "StopWatch represent a running stopwatch", | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "created": { | ||||
|           "type": "string", | ||||
|           "format": "date-time", | ||||
|           "x-go-name": "Created" | ||||
|         }, | ||||
|         "issue_index": { | ||||
|           "type": "integer", | ||||
|           "format": "int64", | ||||
|           "x-go-name": "IssueIndex" | ||||
|         } | ||||
|       }, | ||||
|       "x-go-package": "code.gitea.io/gitea/modules/structs" | ||||
|     }, | ||||
|     "Tag": { | ||||
|       "description": "Tag represents a repository tag", | ||||
|       "type": "object", | ||||
| @ -11553,6 +11643,21 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "StopWatch": { | ||||
|       "description": "StopWatch", | ||||
|       "schema": { | ||||
|         "$ref": "#/definitions/StopWatch" | ||||
|       } | ||||
|     }, | ||||
|     "StopWatchList": { | ||||
|       "description": "StopWatchList", | ||||
|       "schema": { | ||||
|         "type": "array", | ||||
|         "items": { | ||||
|           "$ref": "#/definitions/StopWatch" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "Tag": { | ||||
|       "description": "Tag", | ||||
|       "schema": { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user