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 <xiaolunwen@gmail.com>
This commit is contained in:
parent
f8dcc5f9f8
commit
14a9687444
6 changed files with 234 additions and 32 deletions
|
@ -44,6 +44,18 @@ func TestAPIGetTrackedTimes(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, user.Name, apiTimes[i].UserName)
|
assert.Equal(t, user.Name, apiTimes[i].UserName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test filter
|
||||||
|
since := "2000-01-01T00%3A00%3A02%2B00%3A00" //946684802
|
||||||
|
before := "2000-01-01T00%3A00%3A12%2B00%3A00" //946684812
|
||||||
|
|
||||||
|
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)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
var filterAPITimes api.TrackedTimeList
|
||||||
|
DecodeJSON(t, resp, &filterAPITimes)
|
||||||
|
assert.Len(t, filterAPITimes, 2)
|
||||||
|
assert.Equal(t, int64(3), filterAPITimes[0].ID)
|
||||||
|
assert.Equal(t, int64(6), filterAPITimes[1].ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIDeleteTrackedTime(t *testing.T) {
|
func TestAPIDeleteTrackedTime(t *testing.T) {
|
||||||
|
|
|
@ -100,10 +100,12 @@ func (tl TrackedTimeList) APIFormat() api.TrackedTimeList {
|
||||||
|
|
||||||
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
|
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
|
||||||
type FindTrackedTimesOptions struct {
|
type FindTrackedTimesOptions struct {
|
||||||
IssueID int64
|
IssueID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
RepositoryID int64
|
RepositoryID int64
|
||||||
MilestoneID int64
|
MilestoneID int64
|
||||||
|
CreatedAfterUnix int64
|
||||||
|
CreatedBeforeUnix int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToCond will convert each condition into a xorm-Cond
|
// ToCond will convert each condition into a xorm-Cond
|
||||||
|
@ -121,6 +123,12 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond {
|
||||||
if opts.MilestoneID != 0 {
|
if opts.MilestoneID != 0 {
|
||||||
cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID})
|
cond = cond.And(builder.Eq{"issue.milestone_id": opts.MilestoneID})
|
||||||
}
|
}
|
||||||
|
if opts.CreatedAfterUnix != 0 {
|
||||||
|
cond = cond.And(builder.Gte{"tracked_time.created_unix": opts.CreatedAfterUnix})
|
||||||
|
}
|
||||||
|
if opts.CreatedBeforeUnix != 0 {
|
||||||
|
cond = cond.And(builder.Lte{"tracked_time.created_unix": opts.CreatedBeforeUnix})
|
||||||
|
}
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/times", func() {
|
m.Group("/times", func() {
|
||||||
m.Combo("").Get(repo.ListTrackedTimesByRepository)
|
m.Combo("").Get(repo.ListTrackedTimesByRepository)
|
||||||
m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
|
m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
|
||||||
}, mustEnableIssues)
|
}, mustEnableIssues, reqToken())
|
||||||
m.Group("/issues", func() {
|
m.Group("/issues", func() {
|
||||||
m.Combo("").Get(repo.ListIssues).
|
m.Combo("").Get(repo.ListIssues).
|
||||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
||||||
|
@ -688,12 +688,12 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
|
m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
|
||||||
})
|
})
|
||||||
m.Group("/times", func() {
|
m.Group("/times", func() {
|
||||||
m.Combo("", reqToken()).
|
m.Combo("").
|
||||||
Get(repo.ListTrackedTimes).
|
Get(repo.ListTrackedTimes).
|
||||||
Post(bind(api.AddTimeOption{}), repo.AddTime).
|
Post(bind(api.AddTimeOption{}), repo.AddTime).
|
||||||
Delete(repo.ResetIssueTime)
|
Delete(repo.ResetIssueTime)
|
||||||
m.Delete("/:id", reqToken(), repo.DeleteTime)
|
m.Delete("/:id", repo.DeleteTime)
|
||||||
})
|
}, reqToken())
|
||||||
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
|
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
|
||||||
m.Group("/stopwatch", func() {
|
m.Group("/stopwatch", func() {
|
||||||
m.Post("/start", reqToken(), repo.StartIssueStopwatch)
|
m.Post("/start", reqToken(), repo.StartIssueStopwatch)
|
||||||
|
|
|
@ -5,12 +5,15 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListTrackedTimes list all the tracked times of an issue
|
// ListTrackedTimes list all the tracked times of an issue
|
||||||
|
@ -37,6 +40,16 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
||||||
// type: integer
|
// type: integer
|
||||||
// format: int64
|
// format: int64
|
||||||
// required: true
|
// required: true
|
||||||
|
// - name: since
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
|
// - name: before
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/TrackedTimeList"
|
// "$ref": "#/responses/TrackedTimeList"
|
||||||
|
@ -62,6 +75,11 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
|
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
|
||||||
opts.UserID = ctx.User.ID
|
opts.UserID = ctx.User.ID
|
||||||
}
|
}
|
||||||
|
@ -141,7 +159,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
|
||||||
//allow only RepoAdmin, Admin and User to add time
|
//allow only RepoAdmin, Admin and User to add time
|
||||||
user, err = models.GetUserByName(form.User)
|
user, err = models.GetUserByName(form.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "GetUserByName", err)
|
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,33 +213,33 @@ func ResetIssueTime(ctx *context.APIContext) {
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrIssueNotExist(err) {
|
if models.IsErrIssueNotExist(err) {
|
||||||
ctx.NotFound(err)
|
ctx.NotFound(err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(500, "GetIssueByIndex", err)
|
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
|
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
|
||||||
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
||||||
ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
|
ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Status(403)
|
ctx.Status(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.DeleteIssueUserTimes(issue, ctx.User)
|
err = models.DeleteIssueUserTimes(issue, ctx.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrNotExist(err) {
|
if models.IsErrNotExist(err) {
|
||||||
ctx.Error(404, "DeleteIssueUserTimes", err)
|
ctx.Error(http.StatusNotFound, "DeleteIssueUserTimes", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(500, "DeleteIssueUserTimes", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteIssueUserTimes", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -266,52 +284,53 @@ func DeleteTime(ctx *context.APIContext) {
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrIssueNotExist(err) {
|
if models.IsErrIssueNotExist(err) {
|
||||||
ctx.NotFound(err)
|
ctx.NotFound(err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(500, "GetIssueByIndex", err)
|
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
|
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
|
||||||
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
||||||
ctx.JSON(400, struct{ Message string }{Message: "time tracking disabled"})
|
ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Status(403)
|
ctx.Status(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
|
time, err := models.GetTrackedTimeByID(ctx.ParamsInt64(":id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "GetTrackedTimeByID", err)
|
ctx.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
|
if !ctx.User.IsAdmin && time.UserID != ctx.User.ID {
|
||||||
//Only Admin and User itself can delete their time
|
//Only Admin and User itself can delete their time
|
||||||
ctx.Status(403)
|
ctx.Status(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = models.DeleteTime(time)
|
err = models.DeleteTime(time)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "DeleteTime", err)
|
ctx.Error(http.StatusInternalServerError, "DeleteTime", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Status(204)
|
ctx.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTrackedTimesByUser lists all tracked times of the user
|
// ListTrackedTimesByUser lists all tracked times of the user
|
||||||
func ListTrackedTimesByUser(ctx *context.APIContext) {
|
func ListTrackedTimesByUser(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/times/{user} user userTrackedTimes
|
// swagger:operation GET /repos/{owner}/{repo}/times/{user} repository userTrackedTimes
|
||||||
// ---
|
// ---
|
||||||
// summary: List a user's tracked times in a repo
|
// summary: List a user's tracked times in a repo
|
||||||
|
// deprecated: true
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// parameters:
|
// parameters:
|
||||||
|
@ -335,6 +354,8 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
|
||||||
// "$ref": "#/responses/TrackedTimeList"
|
// "$ref": "#/responses/TrackedTimeList"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
||||||
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
|
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
|
||||||
|
@ -353,9 +374,23 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{
|
|
||||||
|
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
|
||||||
|
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
|
||||||
|
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := models.FindTrackedTimesOptions{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
RepositoryID: ctx.Repo.Repository.ID})
|
RepositoryID: ctx.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
trackedTimes, err := models.GetTrackedTimes(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
|
ctx.Error(http.StatusInternalServerError, "GetTrackedTimes", err)
|
||||||
return
|
return
|
||||||
|
@ -385,11 +420,27 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
|
||||||
// description: name of the repo
|
// description: name of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
|
// - name: user
|
||||||
|
// in: query
|
||||||
|
// description: optional filter by user
|
||||||
|
// type: string
|
||||||
|
// - name: since
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
|
// - name: before
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/TrackedTimeList"
|
// "$ref": "#/responses/TrackedTimeList"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
if !ctx.Repo.Repository.IsTimetrackerEnabled() {
|
||||||
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
|
ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
|
||||||
|
@ -400,8 +451,30 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
|
||||||
RepositoryID: ctx.Repo.Repository.ID,
|
RepositoryID: ctx.Repo.Repository.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
qUser := strings.Trim(ctx.Query("user"), " ")
|
||||||
|
if qUser != "" {
|
||||||
|
user, err := models.GetUserByName(qUser)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opts.UserID = user.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
|
if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
|
||||||
opts.UserID = ctx.User.ID
|
if opts.UserID == 0 {
|
||||||
|
opts.UserID = ctx.User.ID
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trackedTimes, err := models.GetTrackedTimes(opts)
|
trackedTimes, err := models.GetTrackedTimes(opts)
|
||||||
|
@ -423,18 +496,39 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
|
||||||
// summary: List the current user's tracked times
|
// summary: List the current user's tracked times
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: since
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
|
// - name: before
|
||||||
|
// in: query
|
||||||
|
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
|
||||||
|
// type: string
|
||||||
|
// format: date-time
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/TrackedTimeList"
|
// "$ref": "#/responses/TrackedTimeList"
|
||||||
|
|
||||||
trackedTimes, err := models.GetTrackedTimes(models.FindTrackedTimesOptions{UserID: ctx.User.ID})
|
opts := models.FindTrackedTimesOptions{UserID: ctx.User.ID}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
trackedTimes, err := models.GetTrackedTimes(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
|
ctx.Error(http.StatusInternalServerError, "GetTrackedTimesByUser", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = trackedTimes.LoadAttributes(); err != nil {
|
if err = trackedTimes.LoadAttributes(); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
|
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "code.gitea.io/gitea/modules/context"
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
)
|
||||||
|
|
||||||
// UserID user ID of authenticated user, or 0 if not authenticated
|
// UserID user ID of authenticated user, or 0 if not authenticated
|
||||||
func UserID(ctx *context.APIContext) int64 {
|
func UserID(ctx *context.APIContext) int64 {
|
||||||
|
@ -13,3 +18,29 @@ func UserID(ctx *context.APIContext) int64 {
|
||||||
}
|
}
|
||||||
return ctx.User.ID
|
return ctx.User.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
|
||||||
|
func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err error) {
|
||||||
|
qCreatedBefore := strings.Trim(ctx.Query("before"), " ")
|
||||||
|
if qCreatedBefore != "" {
|
||||||
|
createdBefore, err := time.Parse(time.RFC3339, qCreatedBefore)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if !createdBefore.IsZero() {
|
||||||
|
before = createdBefore.Unix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qCreatedAfter := strings.Trim(ctx.Query("since"), " ")
|
||||||
|
if qCreatedAfter != "" {
|
||||||
|
createdAfter, err := time.Parse(time.RFC3339, qCreatedAfter)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if !createdAfter.IsZero() {
|
||||||
|
since = createdAfter.Unix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return before, since, nil
|
||||||
|
}
|
||||||
|
|
|
@ -4433,6 +4433,20 @@
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "since",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "before",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -4543,7 +4557,7 @@
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
},
|
},
|
||||||
"403": {
|
"403": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/forbidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4601,7 +4615,7 @@
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
},
|
},
|
||||||
"403": {
|
"403": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/forbidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6419,6 +6433,26 @@
|
||||||
"name": "repo",
|
"name": "repo",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "optional filter by user",
|
||||||
|
"name": "user",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "since",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "before",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -6427,6 +6461,9 @@
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6437,10 +6474,11 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"user"
|
"repository"
|
||||||
],
|
],
|
||||||
"summary": "List a user's tracked times in a repo",
|
"summary": "List a user's tracked times in a repo",
|
||||||
"operationId": "userTrackedTimes",
|
"operationId": "userTrackedTimes",
|
||||||
|
"deprecated": true,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -6470,6 +6508,9 @@
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7685,6 +7726,22 @@
|
||||||
],
|
],
|
||||||
"summary": "List the current user's tracked times",
|
"summary": "List the current user's tracked times",
|
||||||
"operationId": "userCurrentTrackedTimes",
|
"operationId": "userCurrentTrackedTimes",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated after the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "since",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"description": "Only show times updated before the given time. This is a timestamp in RFC 3339 format",
|
||||||
|
"name": "before",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/TrackedTimeList"
|
"$ref": "#/responses/TrackedTimeList"
|
||||||
|
|
Reference in a new issue