Skip to content

Commit 14a9687

Browse files
6543lunny
authored andcommitted
times Add filters (#9373)
(extend #9200) * add query param for GET functions (created Bevore & after) * add test * generalize func GetQueryBeforeSince Co-authored-by: Lunny Xiao <[email protected]>
1 parent f8dcc5f commit 14a9687

File tree

6 files changed

+234
-32
lines changed

6 files changed

+234
-32
lines changed

integrations/api_issue_tracked_time_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ func TestAPIGetTrackedTimes(t *testing.T) {
4444
assert.NoError(t, err)
4545
assert.Equal(t, user.Name, apiTimes[i].UserName)
4646
}
47+
48+
// test filter
49+
since := "2000-01-01T00%3A00%3A02%2B00%3A00" //946684802
50+
before := "2000-01-01T00%3A00%3A12%2B00%3A00" //946684812
51+
52+
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?since=%s&before=%s&token=%s", user2.Name, issue2.Repo.Name, issue2.Index, since, before, token)
53+
resp = session.MakeRequest(t, req, http.StatusOK)
54+
var filterAPITimes api.TrackedTimeList
55+
DecodeJSON(t, resp, &filterAPITimes)
56+
assert.Len(t, filterAPITimes, 2)
57+
assert.Equal(t, int64(3), filterAPITimes[0].ID)
58+
assert.Equal(t, int64(6), filterAPITimes[1].ID)
4759
}
4860

4961
func TestAPIDeleteTrackedTime(t *testing.T) {

models/issue_tracked_time.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ func (tl TrackedTimeList) APIFormat() api.TrackedTimeList {
100100

101101
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
102102
type FindTrackedTimesOptions struct {
103-
IssueID int64
104-
UserID int64
105-
RepositoryID int64
106-
MilestoneID int64
103+
IssueID int64
104+
UserID int64
105+
RepositoryID int64
106+
MilestoneID int64
107+
CreatedAfterUnix int64
108+
CreatedBeforeUnix int64
107109
}
108110

109111
// ToCond will convert each condition into a xorm-Cond
@@ -121,6 +123,12 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond {
121123
if opts.MilestoneID != 0 {
122124
cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID})
123125
}
126+
if opts.CreatedAfterUnix != 0 {
127+
cond = cond.And(builder.Gte{"tracked_time.created_unix": opts.CreatedAfterUnix})
128+
}
129+
if opts.CreatedBeforeUnix != 0 {
130+
cond = cond.And(builder.Lte{"tracked_time.created_unix": opts.CreatedBeforeUnix})
131+
}
124132
return cond
125133
}
126134

routers/api/v1/api.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) {
654654
m.Group("/times", func() {
655655
m.Combo("").Get(repo.ListTrackedTimesByRepository)
656656
m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
657-
}, mustEnableIssues)
657+
}, mustEnableIssues, reqToken())
658658
m.Group("/issues", func() {
659659
m.Combo("").Get(repo.ListIssues).
660660
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
@@ -688,12 +688,12 @@ func RegisterRoutes(m *macaron.Macaron) {
688688
m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
689689
})
690690
m.Group("/times", func() {
691-
m.Combo("", reqToken()).
691+
m.Combo("").
692692
Get(repo.ListTrackedTimes).
693693
Post(bind(api.AddTimeOption{}), repo.AddTime).
694694
Delete(repo.ResetIssueTime)
695-
m.Delete("/:id", reqToken(), repo.DeleteTime)
696-
})
695+
m.Delete("/:id", repo.DeleteTime)
696+
}, reqToken())
697697
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
698698
m.Group("/stopwatch", func() {
699699
m.Post("/start", reqToken(), repo.StartIssueStopwatch)

routers/api/v1/repo/issue_tracked_time.go

+114-20
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package repo
66

77
import (
8+
"fmt"
89
"net/http"
10+
"strings"
911
"time"
1012

1113
"code.gitea.io/gitea/models"
1214
"code.gitea.io/gitea/modules/context"
1315
api "code.gitea.io/gitea/modules/structs"
16+
"code.gitea.io/gitea/routers/api/v1/utils"
1417
)
1518

1619
// ListTrackedTimes list all the tracked times of an issue
@@ -37,6 +40,16 @@ func ListTrackedTimes(ctx *context.APIContext) {
3740
// type: integer
3841
// format: int64
3942
// required: true
43+
// - name: since
44+
// in: query
45+
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
46+
// type: string
47+
// format: date-time
48+
// - name: before
49+
// in: query
50+
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
51+
// type: string
52+
// format: date-time
4053
// responses:
4154
// "200":
4255
// "$ref": "#/responses/TrackedTimeList"
@@ -62,6 +75,11 @@ func ListTrackedTimes(ctx *context.APIContext) {
6275
IssueID: issue.ID,
6376
}
6477

78+
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
79+
ctx.InternalServerError(err)
80+
return
81+
}
82+
6583
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
6684
opts.UserID = ctx.User.ID
6785
}
@@ -141,7 +159,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
141159
//allow only RepoAdmin, Admin and User to add time
142160
user, err = models.GetUserByName(form.User)
143161
if err != nil {
144-
ctx.Error(500, "GetUserByName", err)
162+
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
145163
}
146164
}
147165
}
@@ -195,33 +213,33 @@ func ResetIssueTime(ctx *context.APIContext) {
195213
// "400":
196214
// "$ref": "#/responses/error"
197215
// "403":
198-
// "$ref": "#/responses/error"
216+
// "$ref": "#/responses/forbidden"
199217

200218
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
201219
if err != nil {
202220
if models.IsErrIssueNotExist(err) {
203221
ctx.NotFound(err)
204222
} else {
205-
ctx.Error(500, "GetIssueByIndex", err)
223+
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
206224
}
207225
return
208226
}
209227

210228
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
211229
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
212-
ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
230+
ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
213231
return
214232
}
215-
ctx.Status(403)
233+
ctx.Status(http.StatusForbidden)
216234
return
217235
}
218236

219237
err = models.DeleteIssueUserTimes(issue, ctx.User)
220238
if err != nil {
221239
if models.IsErrNotExist(err) {
222-
ctx.Error(404, "DeleteIssueUserTimes", err)
240+
ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
223241
} else {
224-
ctx.Error(500, "DeleteIssueUserTimes", err)
242+
ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
225243
}
226244
return
227245
}
@@ -266,52 +284,53 @@ func DeleteTime(ctx *context.APIContext) {
266284
// "400":
267285
// "$ref": "#/responses/error"
268286
// "403":
269-
// "$ref": "#/responses/error"
287+
// "$ref": "#/responses/forbidden"
270288

271289
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
272290
if err != nil {
273291
if models.IsErrIssueNotExist(err) {
274292
ctx.NotFound(err)
275293
} else {
276-
ctx.Error(500, "GetIssueByIndex", err)
294+
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
277295
}
278296
return
279297
}
280298

281299
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
282300
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
283-
ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
301+
ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
284302
return
285303
}
286-
ctx.Status(403)
304+
ctx.Status(http.StatusForbidden)
287305
return
288306
}
289307

290308
time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
291309
if err != nil {
292-
ctx.Error(500, "GetTrackedTimeByID", err)
310+
ctx.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err)
293311
return
294312
}
295313

296314
if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
297315
//Only Admin and User itself can delete their time
298-
ctx.Status(403)
316+
ctx.Status(http.StatusForbidden)
299317
return
300318
}
301319

302320
err = models.DeleteTime(time)
303321
if err != nil {
304-
ctx.Error(500, "DeleteTime", err)
322+
ctx.Error(http.StatusInternalServerError, "DeleteTime", err)
305323
return
306324
}
307-
ctx.Status(204)
325+
ctx.Status(http.StatusNoContent)
308326
}
309327

310328
// ListTrackedTimesByUser lists all tracked times of the user
311329
func ListTrackedTimesByUser(ctx *context.APIContext) {
312-
// swagger:operation GET /repos/{owner}/{repo}/times/{user} user userTrackedTimes
330+
// swagger:operation GET /repos/{owner}/{repo}/times/{user} repository userTrackedTimes
313331
// ---
314332
// summary: List a user's tracked times in a repo
333+
// deprecated: true
315334
// produces:
316335
// - application/json
317336
// parameters:
@@ -335,6 +354,8 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
335354
// "$ref": "#/responses/TrackedTimeList"
336355
// "400":
337356
// "$ref": "#/responses/error"
357+
// "403":
358+
// "$ref": "#/responses/forbidden"
338359

339360
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
340361
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
@@ -353,9 +374,23 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
353374
ctx.NotFound()
354375
return
355376
}
356-
trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{
377+
378+
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
379+
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
380+
return
381+
}
382+
383+
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
384+
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
385+
return
386+
}
387+
388+
opts := models.FindTrackedTimesOptions{
357389
UserID: user.ID,
358-
RepositoryID: ctx.Repo.Repository.ID})
390+
RepositoryID: ctx.Repo.Repository.ID,
391+
}
392+
393+
trackedTimes, err := models.GetTrackedTimes(opts)
359394
if err != nil {
360395
ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
361396
return
@@ -385,11 +420,27 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
385420
// description: name of the repo
386421
// type: string
387422
// required: true
423+
// - name: user
424+
// in: query
425+
// description: optional filter by user
426+
// type: string
427+
// - name: since
428+
// in: query
429+
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
430+
// type: string
431+
// format: date-time
432+
// - name: before
433+
// in: query
434+
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
435+
// type: string
436+
// format: date-time
388437
// responses:
389438
// "200":
390439
// "$ref": "#/responses/TrackedTimeList"
391440
// "400":
392441
// "$ref": "#/responses/error"
442+
// "403":
443+
// "$ref": "#/responses/forbidden"
393444

394445
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
395446
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
@@ -400,8 +451,30 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
400451
RepositoryID: ctx.Repo.Repository.ID,
401452
}
402453

454+
// Filters
455+
qUser := strings.Trim(ctx.Query("user"), " ")
456+
if qUser != "" {
457+
user, err := models.GetUserByName(qUser)
458+
if err != nil {
459+
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
460+
return
461+
}
462+
opts.UserID = user.ID
463+
}
464+
465+
var err error
466+
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
467+
ctx.InternalServerError(err)
468+
return
469+
}
470+
403471
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
404-
opts.UserID = ctx.User.ID
472+
if opts.UserID == 0 {
473+
opts.UserID = ctx.User.ID
474+
} else {
475+
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
476+
return
477+
}
405478
}
406479

407480
trackedTimes, err := models.GetTrackedTimes(opts)
@@ -423,18 +496,39 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
423496
// summary: List the current user's tracked times
424497
// produces:
425498
// - application/json
499+
// parameters:
500+
// - name: since
501+
// in: query
502+
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
503+
// type: string
504+
// format: date-time
505+
// - name: before
506+
// in: query
507+
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
508+
// type: string
509+
// format: date-time
426510
// responses:
427511
// "200":
428512
// "$ref": "#/responses/TrackedTimeList"
429513

430-
trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{UserID: ctx.User.ID})
514+
opts := models.FindTrackedTimesOptions{UserID: ctx.User.ID}
515+
516+
var err error
517+
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
518+
ctx.InternalServerError(err)
519+
return
520+
}
521+
522+
trackedTimes, err := models.GetTrackedTimes(opts)
431523
if err != nil {
432524
ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
433525
return
434526
}
527+
435528
if err = trackedTimes.LoadAttributes(); err != nil {
436529
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
437530
return
438531
}
532+
439533
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
440534
}

0 commit comments

Comments
 (0)