Skip to content

Commit aceb108

Browse files
6543techknowlogick
authored andcommitted
[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
1 parent 382936a commit aceb108

File tree

9 files changed

+482
-141
lines changed

9 files changed

+482
-141
lines changed
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"net/http"
9+
"testing"
10+
11+
"code.gitea.io/gitea/models"
12+
api "code.gitea.io/gitea/modules/structs"
13+
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestAPIListStopWatches(t *testing.T) {
18+
defer prepareTestEnv(t)()
19+
20+
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
21+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
22+
23+
session := loginUser(t, owner.Name)
24+
token := getTokenForLoggedInUser(t, session)
25+
req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token)
26+
resp := session.MakeRequest(t, req, http.StatusOK)
27+
var apiWatches []*api.StopWatch
28+
DecodeJSON(t, resp, &apiWatches)
29+
expect := models.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch)
30+
expectAPI, _ := expect.APIFormat()
31+
assert.Len(t, apiWatches, 1)
32+
33+
assert.EqualValues(t, expectAPI.IssueIndex, apiWatches[0].IssueIndex)
34+
assert.EqualValues(t, expectAPI.Created.Unix(), apiWatches[0].Created.Unix())
35+
}
36+
37+
func TestAPIStopStopWatches(t *testing.T) {
38+
defer prepareTestEnv(t)()
39+
40+
issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
41+
_ = issue.LoadRepo()
42+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
43+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
44+
45+
session := loginUser(t, user.Name)
46+
token := getTokenForLoggedInUser(t, session)
47+
48+
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token)
49+
session.MakeRequest(t, req, http.StatusCreated)
50+
session.MakeRequest(t, req, http.StatusConflict)
51+
}
52+
53+
func TestAPICancelStopWatches(t *testing.T) {
54+
defer prepareTestEnv(t)()
55+
56+
issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
57+
_ = issue.LoadRepo()
58+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
59+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
60+
61+
session := loginUser(t, user.Name)
62+
token := getTokenForLoggedInUser(t, session)
63+
64+
req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token)
65+
session.MakeRequest(t, req, http.StatusNoContent)
66+
session.MakeRequest(t, req, http.StatusConflict)
67+
}
68+
69+
func TestAPIStartStopWatches(t *testing.T) {
70+
defer prepareTestEnv(t)()
71+
72+
issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
73+
_ = issue.LoadRepo()
74+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
75+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
76+
77+
session := loginUser(t, user.Name)
78+
token := getTokenForLoggedInUser(t, session)
79+
80+
req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token)
81+
session.MakeRequest(t, req, http.StatusCreated)
82+
session.MakeRequest(t, req, http.StatusConflict)
83+
}

models/fixtures/stopwatch.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
id: 1
33
user_id: 1
44
issue_id: 1
5-
created_unix: 1500988502
5+
created_unix: 1500988001
66

77
-
88
id: 2
99
user_id: 2
1010
issue_id: 2
11-
created_unix: 1500988502
11+
created_unix: 1500988002

models/issue_stopwatch.go

+39
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"time"
1010

11+
api "code.gitea.io/gitea/modules/structs"
1112
"code.gitea.io/gitea/modules/timeutil"
1213
)
1314

@@ -19,6 +20,9 @@ type Stopwatch struct {
1920
CreatedUnix timeutil.TimeStamp `xorm:"created"`
2021
}
2122

23+
// Stopwatches is a List ful of Stopwatch
24+
type Stopwatches []Stopwatch
25+
2226
func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
2327
sw = new(Stopwatch)
2428
exists, err = e.
@@ -28,6 +32,16 @@ func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool,
2832
return
2933
}
3034

35+
// GetUserStopwatches return list of all stopwatches of a user
36+
func GetUserStopwatches(userID int64) (sws *Stopwatches, err error) {
37+
sws = new(Stopwatches)
38+
err = x.Where("stopwatch.user_id = ?", userID).Find(sws)
39+
if err != nil {
40+
return nil, err
41+
}
42+
return sws, nil
43+
}
44+
3145
// StopwatchExists returns true if the stopwatch exists
3246
func StopwatchExists(userID int64, issueID int64) bool {
3347
_, exists, _ := getStopwatch(x, userID, issueID)
@@ -160,3 +174,28 @@ func SecToTime(duration int64) string {
160174

161175
return hrs
162176
}
177+
178+
// APIFormat convert Stopwatch type to api.StopWatch type
179+
func (sw *Stopwatch) APIFormat() (api.StopWatch, error) {
180+
issue, err := getIssueByID(x, sw.IssueID)
181+
if err != nil {
182+
return api.StopWatch{}, err
183+
}
184+
return api.StopWatch{
185+
Created: sw.CreatedUnix.AsTime(),
186+
IssueIndex: issue.Index,
187+
}, nil
188+
}
189+
190+
// APIFormat convert Stopwatches type to api.StopWatches type
191+
func (sws Stopwatches) APIFormat() (api.StopWatches, error) {
192+
result := api.StopWatches(make([]api.StopWatch, 0, len(sws)))
193+
for _, sw := range sws {
194+
apiSW, err := sw.APIFormat()
195+
if err != nil {
196+
return nil, err
197+
}
198+
result = append(result, apiSW)
199+
}
200+
return result, nil
201+
}

modules/structs/issue_stopwatch.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package structs
6+
7+
import (
8+
"time"
9+
)
10+
11+
// StopWatch represent a running stopwatch
12+
type StopWatch struct {
13+
// swagger:strfmt date-time
14+
Created time.Time `json:"created"`
15+
IssueIndex int64 `json:"issue_index"`
16+
}
17+
18+
// StopWatches represent a list of stopwatches
19+
type StopWatches []StopWatch

routers/api/v1/api.go

+3
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ func RegisterRoutes(m *macaron.Macaron) {
584584
})
585585
m.Get("/times", repo.ListMyTrackedTimes)
586586

587+
m.Get("/stopwatches", repo.GetStopwatches)
588+
587589
m.Get("/subscriptions", user.GetMyWatchedRepos)
588590

589591
m.Get("/teams", org.ListUserTeams)
@@ -691,6 +693,7 @@ func RegisterRoutes(m *macaron.Macaron) {
691693
m.Group("/stopwatch", func() {
692694
m.Post("/start", reqToken(), repo.StartIssueStopwatch)
693695
m.Post("/stop", reqToken(), repo.StopIssueStopwatch)
696+
m.Delete("/delete", reqToken(), repo.DeleteIssueStopwatch)
694697
})
695698
m.Group("/subscriptions", func() {
696699
m.Get("", repo.GetIssueSubscribers)

routers/api/v1/repo/issue.go

-138
Original file line numberDiff line numberDiff line change
@@ -598,141 +598,3 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
598598

599599
ctx.JSON(201, api.IssueDeadline{Deadline: &deadline})
600600
}
601-
602-
// StartIssueStopwatch creates a stopwatch for the given issue.
603-
func StartIssueStopwatch(ctx *context.APIContext) {
604-
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch
605-
// ---
606-
// summary: Start stopwatch on an issue.
607-
// consumes:
608-
// - application/json
609-
// produces:
610-
// - application/json
611-
// parameters:
612-
// - name: owner
613-
// in: path
614-
// description: owner of the repo
615-
// type: string
616-
// required: true
617-
// - name: repo
618-
// in: path
619-
// description: name of the repo
620-
// type: string
621-
// required: true
622-
// - name: index
623-
// in: path
624-
// description: index of the issue to create the stopwatch on
625-
// type: integer
626-
// format: int64
627-
// required: true
628-
// responses:
629-
// "201":
630-
// "$ref": "#/responses/empty"
631-
// "403":
632-
// description: Not repo writer, user does not have rights to toggle stopwatch
633-
// "404":
634-
// description: Issue not found
635-
// "409":
636-
// description: Cannot start a stopwatch again if it already exists
637-
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
638-
if err != nil {
639-
if models.IsErrIssueNotExist(err) {
640-
ctx.NotFound()
641-
} else {
642-
ctx.Error(500, "GetIssueByIndex", err)
643-
}
644-
645-
return
646-
}
647-
648-
if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
649-
ctx.Status(403)
650-
return
651-
}
652-
653-
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
654-
ctx.Status(403)
655-
return
656-
}
657-
658-
if models.StopwatchExists(ctx.User.ID, issue.ID) {
659-
ctx.Error(409, "StopwatchExists", "a stopwatch has already been started for this issue")
660-
return
661-
}
662-
663-
if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
664-
ctx.Error(500, "CreateOrStopIssueStopwatch", err)
665-
return
666-
}
667-
668-
ctx.Status(201)
669-
}
670-
671-
// StopIssueStopwatch stops a stopwatch for the given issue.
672-
func StopIssueStopwatch(ctx *context.APIContext) {
673-
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopWatch
674-
// ---
675-
// summary: Stop an issue's existing stopwatch.
676-
// consumes:
677-
// - application/json
678-
// produces:
679-
// - application/json
680-
// parameters:
681-
// - name: owner
682-
// in: path
683-
// description: owner of the repo
684-
// type: string
685-
// required: true
686-
// - name: repo
687-
// in: path
688-
// description: name of the repo
689-
// type: string
690-
// required: true
691-
// - name: index
692-
// in: path
693-
// description: index of the issue to stop the stopwatch on
694-
// type: integer
695-
// format: int64
696-
// required: true
697-
// responses:
698-
// "201":
699-
// "$ref": "#/responses/empty"
700-
// "403":
701-
// description: Not repo writer, user does not have rights to toggle stopwatch
702-
// "404":
703-
// description: Issue not found
704-
// "409":
705-
// description: Cannot stop a non existent stopwatch
706-
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
707-
if err != nil {
708-
if models.IsErrIssueNotExist(err) {
709-
ctx.NotFound()
710-
} else {
711-
ctx.Error(500, "GetIssueByIndex", err)
712-
}
713-
714-
return
715-
}
716-
717-
if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
718-
ctx.Status(403)
719-
return
720-
}
721-
722-
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
723-
ctx.Status(403)
724-
return
725-
}
726-
727-
if !models.StopwatchExists(ctx.User.ID, issue.ID) {
728-
ctx.Error(409, "StopwatchExists", "cannot stop a non existent stopwatch")
729-
return
730-
}
731-
732-
if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
733-
ctx.Error(500, "CreateOrStopIssueStopwatch", err)
734-
return
735-
}
736-
737-
ctx.Status(201)
738-
}

0 commit comments

Comments
 (0)