mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Update milestone counters on new issue. (#16183)
Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		@@ -141,6 +141,12 @@ func (milestone *Milestone) checkForConsistency(t *testing.T) {
 | 
				
			|||||||
	actual := getCount(t, x.Where("is_closed=?", true), &Issue{MilestoneID: milestone.ID})
 | 
						actual := getCount(t, x.Where("is_closed=?", true), &Issue{MilestoneID: milestone.ID})
 | 
				
			||||||
	assert.EqualValues(t, milestone.NumClosedIssues, actual,
 | 
						assert.EqualValues(t, milestone.NumClosedIssues, actual,
 | 
				
			||||||
		"Unexpected number of closed issues for milestone %+v", milestone)
 | 
							"Unexpected number of closed issues for milestone %+v", milestone)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						completeness := 0
 | 
				
			||||||
 | 
						if milestone.NumIssues > 0 {
 | 
				
			||||||
 | 
							completeness = milestone.NumClosedIssues * 100 / milestone.NumIssues
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.Equal(t, completeness, milestone.Completeness)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (label *Label) checkForConsistency(t *testing.T) {
 | 
					func (label *Label) checkForConsistency(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -647,8 +647,10 @@ func (issue *Issue) doChangeStatus(e *xorm.Session, doer *User, isMergePull bool
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update issue count of milestone
 | 
						// Update issue count of milestone
 | 
				
			||||||
	if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
 | 
						if issue.MilestoneID > 0 {
 | 
				
			||||||
		return nil, err
 | 
							if err := updateMilestoneCounters(e, issue.MilestoneID); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := issue.updateClosedNum(e); err != nil {
 | 
						if err := issue.updateClosedNum(e); err != nil {
 | 
				
			||||||
@@ -907,7 +909,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.Issue.MilestoneID > 0 {
 | 
						if opts.Issue.MilestoneID > 0 {
 | 
				
			||||||
		if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
 | 
							if err := updateMilestoneCounters(e, opts.Issue.MilestoneID); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,8 +129,12 @@ func GetMilestoneByRepoIDANDName(repoID int64, name string) (*Milestone, error)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetMilestoneByID returns the milestone via id .
 | 
					// GetMilestoneByID returns the milestone via id .
 | 
				
			||||||
func GetMilestoneByID(id int64) (*Milestone, error) {
 | 
					func GetMilestoneByID(id int64) (*Milestone, error) {
 | 
				
			||||||
 | 
						return getMilestoneByID(x, id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getMilestoneByID(e Engine, id int64) (*Milestone, error) {
 | 
				
			||||||
	var m Milestone
 | 
						var m Milestone
 | 
				
			||||||
	has, err := x.ID(id).Get(&m)
 | 
						has, err := e.ID(id).Get(&m)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	} else if !has {
 | 
						} else if !has {
 | 
				
			||||||
@@ -155,10 +159,6 @@ func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if IsClosed changed, update milestone numbers of repository
 | 
						// if IsClosed changed, update milestone numbers of repository
 | 
				
			||||||
	if oldIsClosed != m.IsClosed {
 | 
						if oldIsClosed != m.IsClosed {
 | 
				
			||||||
		if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
 | 
							if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
 | 
				
			||||||
@@ -171,23 +171,31 @@ func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func updateMilestone(e Engine, m *Milestone) error {
 | 
					func updateMilestone(e Engine, m *Milestone) error {
 | 
				
			||||||
	m.Name = strings.TrimSpace(m.Name)
 | 
						m.Name = strings.TrimSpace(m.Name)
 | 
				
			||||||
	_, err := e.ID(m.ID).AllCols().
 | 
						_, err := e.ID(m.ID).AllCols().Update(m)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return updateMilestoneCounters(e, m.ID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// updateMilestoneCounters calculates NumIssues, NumClosesIssues and Completeness
 | 
				
			||||||
 | 
					func updateMilestoneCounters(e Engine, id int64) error {
 | 
				
			||||||
 | 
						_, err := e.ID(id).
 | 
				
			||||||
		SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
 | 
							SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
 | 
				
			||||||
			builder.Eq{"milestone_id": m.ID},
 | 
								builder.Eq{"milestone_id": id},
 | 
				
			||||||
		)).
 | 
							)).
 | 
				
			||||||
		SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
 | 
							SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
 | 
				
			||||||
			builder.Eq{
 | 
								builder.Eq{
 | 
				
			||||||
				"milestone_id": m.ID,
 | 
									"milestone_id": id,
 | 
				
			||||||
				"is_closed":    true,
 | 
									"is_closed":    true,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		)).
 | 
							)).
 | 
				
			||||||
		Update(m)
 | 
							Update(&Milestone{})
 | 
				
			||||||
	return err
 | 
						if err != nil {
 | 
				
			||||||
}
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
 | 
						_, err = e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
 | 
				
			||||||
	_, err := e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
 | 
							id,
 | 
				
			||||||
		milestoneID,
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -256,25 +264,15 @@ func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilesto
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if oldMilestoneID > 0 {
 | 
						if oldMilestoneID > 0 {
 | 
				
			||||||
		if err := updateMilestoneTotalNum(e, oldMilestoneID); err != nil {
 | 
							if err := updateMilestoneCounters(e, oldMilestoneID); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if issue.IsClosed {
 | 
					 | 
				
			||||||
			if err := updateMilestoneClosedNum(e, oldMilestoneID); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if issue.MilestoneID > 0 {
 | 
						if issue.MilestoneID > 0 {
 | 
				
			||||||
		if err := updateMilestoneTotalNum(e, issue.MilestoneID); err != nil {
 | 
							if err := updateMilestoneCounters(e, issue.MilestoneID); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if issue.IsClosed {
 | 
					 | 
				
			||||||
			if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if oldMilestoneID > 0 || issue.MilestoneID > 0 {
 | 
						if oldMilestoneID > 0 || issue.MilestoneID > 0 {
 | 
				
			||||||
@@ -622,29 +620,6 @@ func updateRepoMilestoneNum(e Engine, repoID int64) error {
 | 
				
			|||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateMilestoneTotalNum(e Engine, milestoneID int64) (err error) {
 | 
					 | 
				
			||||||
	if _, err = e.Exec("UPDATE `milestone` SET num_issues=(SELECT count(*) FROM issue WHERE milestone_id=?) WHERE id=?",
 | 
					 | 
				
			||||||
		milestoneID,
 | 
					 | 
				
			||||||
		milestoneID,
 | 
					 | 
				
			||||||
	); err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return updateMilestoneCompleteness(e, milestoneID)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func updateMilestoneClosedNum(e Engine, milestoneID int64) (err error) {
 | 
					 | 
				
			||||||
	if _, err = e.Exec("UPDATE `milestone` SET num_closed_issues=(SELECT count(*) FROM issue WHERE milestone_id=? AND is_closed=?) WHERE id=?",
 | 
					 | 
				
			||||||
		milestoneID,
 | 
					 | 
				
			||||||
		true,
 | 
					 | 
				
			||||||
		milestoneID,
 | 
					 | 
				
			||||||
	); err != nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return updateMilestoneCompleteness(e, milestoneID)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//  _____               _            _ _____ _
 | 
					//  _____               _            _ _____ _
 | 
				
			||||||
// |_   _| __ __ _  ___| | _____  __| |_   _(_)_ __ ___   ___  ___
 | 
					// |_   _| __ __ _  ___| | _____  __| |_   _(_)_ __ ___   ___  ___
 | 
				
			||||||
//   | || '__/ _` |/ __| |/ / _ \/ _` | | | | | '_ ` _ \ / _ \/ __|
 | 
					//   | || '__/ _` |/ __| |/ / _ \/ _` | | | | | '_ ` _ \ / _ \/ __|
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,7 +215,7 @@ func TestChangeMilestoneStatus(t *testing.T) {
 | 
				
			|||||||
	CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
 | 
						CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpdateMilestoneClosedNum(t *testing.T) {
 | 
					func TestUpdateMilestoneCounters(t *testing.T) {
 | 
				
			||||||
	assert.NoError(t, PrepareTestDatabase())
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
	issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
 | 
						issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
 | 
				
			||||||
		"is_closed=0").(*Issue)
 | 
							"is_closed=0").(*Issue)
 | 
				
			||||||
@@ -224,14 +224,14 @@ func TestUpdateMilestoneClosedNum(t *testing.T) {
 | 
				
			|||||||
	issue.ClosedUnix = timeutil.TimeStampNow()
 | 
						issue.ClosedUnix = timeutil.TimeStampNow()
 | 
				
			||||||
	_, err := x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
 | 
						_, err := x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 | 
						assert.NoError(t, updateMilestoneCounters(x, issue.MilestoneID))
 | 
				
			||||||
	CheckConsistencyFor(t, &Milestone{})
 | 
						CheckConsistencyFor(t, &Milestone{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue.IsClosed = false
 | 
						issue.IsClosed = false
 | 
				
			||||||
	issue.ClosedUnix = 0
 | 
						issue.ClosedUnix = 0
 | 
				
			||||||
	_, err = x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
 | 
						_, err = x.ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 | 
						assert.NoError(t, updateMilestoneCounters(x, issue.MilestoneID))
 | 
				
			||||||
	CheckConsistencyFor(t, &Milestone{})
 | 
						CheckConsistencyFor(t, &Milestone{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user