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

Fix milestone deadline and date related problems #32339

Merged
merged 11 commits into from
Nov 5, 2024
15 changes: 13 additions & 2 deletions models/issues/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (

"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"

"xorm.io/builder"
"xorm.io/xorm"
)

// ErrMilestoneNotExist represents a "MilestoneNotExist" kind of error.
Expand Down Expand Up @@ -82,9 +84,18 @@ func (m *Milestone) BeforeUpdate() {

// AfterLoad is invoked from XORM after setting the value of a field of
// this object.
func (m *Milestone) AfterLoad() {
func (m *Milestone) AfterLoad(session *xorm.Session) {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
if m.DeadlineUnix.Year() == 9999 {
if m.DeadlineUnix == 0 {
return
}
// for legacy reasons, all years after 9000 are considered as no deadline
if m.DeadlineUnix.Year() > 9000 {
m.DeadlineUnix = 0
m.IsOverdue = false
if _, err := session.ID(m.ID).NoAutoTime().Cols("deadline_unix", "is_overdue").Update(m); err != nil {
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
log.Error("AfterLoad update legacy deadline failed: %v", err)
}
lunny marked this conversation as resolved.
Show resolved Hide resolved
return
}

Expand Down
9 changes: 4 additions & 5 deletions routers/api/v1/repo/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package repo
import (
"net/http"
"strconv"
"time"

"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
Expand Down Expand Up @@ -155,16 +154,16 @@ func CreateMilestone(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.CreateMilestoneOption)

if form.Deadline == nil {
defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
form.Deadline = &defaultDeadline
var deadlineUnix int64
if form.Deadline != nil {
deadlineUnix = form.Deadline.Unix()
}

milestone := &issues_model.Milestone{
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Content: form.Description,
DeadlineUnix: timeutil.TimeStamp(form.Deadline.Unix()),
DeadlineUnix: timeutil.TimeStamp(deadlineUnix),
}

if form.State == "closed" {
Expand Down
46 changes: 25 additions & 21 deletions routers/web/repo/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,22 +134,24 @@ func NewMilestonePost(ctx *context.Context) {
return
}

if len(form.Deadline) == 0 {
form.Deadline = "9999-12-31"
}
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
var deadlineUnix int64
if len(form.Deadline) > 0 {
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
deadlineUnix = deadline.Unix()
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
if err = issues_model.NewMilestone(ctx, &issues_model.Milestone{
if err := issues_model.NewMilestone(ctx, &issues_model.Milestone{
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Content: form.Content,
DeadlineUnix: timeutil.TimeStamp(deadline.Unix()),
DeadlineUnix: timeutil.TimeStamp(deadlineUnix),
}); err != nil {
ctx.ServerError("NewMilestone", err)
return
Expand Down Expand Up @@ -194,17 +196,19 @@ func EditMilestonePost(ctx *context.Context) {
return
}

if len(form.Deadline) == 0 {
form.Deadline = "9999-12-31"
}
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
var deadlineUnix int64
if len(form.Deadline) > 0 {
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
deadlineUnix = deadline.Unix()
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":id"))
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
Expand All @@ -216,7 +220,7 @@ func EditMilestonePost(ctx *context.Context) {
}
m.Name = form.Title
m.Content = form.Content
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
m.DeadlineUnix = timeutil.TimeStamp(deadlineUnix)
if err = issues_model.UpdateMilestone(ctx, m, m.IsClosed); err != nil {
ctx.ServerError("UpdateMilestone", err)
return
Expand Down
5 changes: 4 additions & 1 deletion services/convert/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,10 @@ func ToAPIMilestone(m *issues_model.Milestone) *api.Milestone {
if m.IsClosed {
apiMilestone.Closed = m.ClosedDateUnix.AsTimePtr()
}
if m.DeadlineUnix.Year() < 9999 {
// for legacy reasons, all years after 9000 are considered as no deadline
if m.DeadlineUnix.Year() > 9000 || m.DeadlineUnix == 0 {
apiMilestone.Deadline = nil
} else {
apiMilestone.Deadline = m.DeadlineUnix.AsTimePtr()
}
return apiMilestone
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/api_issue_milestone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"testing"
"time"

auth_model "code.gitea.io/gitea/models/auth"
issues_model "code.gitea.io/gitea/models/issues"
Expand Down Expand Up @@ -59,13 +60,15 @@ func TestAPIIssuesMilestone(t *testing.T) {
DecodeJSON(t, resp, &apiMilestone)
assert.Equal(t, "wow", apiMilestone.Title)
assert.Equal(t, structs.StateClosed, apiMilestone.State)
assert.Equal(t, (*time.Time)(nil), apiMilestone.Deadline)

var apiMilestones []structs.Milestone
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s", owner.Name, repo.Name, "all")).
AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiMilestones)
assert.Len(t, apiMilestones, 4)
assert.Equal(t, (*time.Time)(nil), apiMilestones[0].Deadline)

req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%s", owner.Name, repo.Name, apiMilestones[2].Title)).
AddTokenAuth(token)
Expand Down
Loading