Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API] extend StopWatch #9196

Merged
merged 13 commits into from
Dec 12, 2019
83 changes: 83 additions & 0 deletions integrations/api_issue_stopwatch_test.go
Original file line number Diff line number Diff line change
@@ -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.StatusAccepted)
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)
}
4 changes: 2 additions & 2 deletions models/fixtures/stopwatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
39 changes: 39 additions & 0 deletions models/issue_stopwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"time"

api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
)

Expand All @@ -28,6 +29,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)
Expand Down Expand Up @@ -160,3 +171,31 @@ 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
}

// Stopwatches is a List ful of Stopwatch
type Stopwatches []Stopwatch
6543 marked this conversation as resolved.
Show resolved Hide resolved

// 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 changes: 19 additions & 0 deletions modules/structs/issue_stopwatch.go
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
138 changes: 0 additions & 138 deletions routers/api/v1/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Loading