From 1bbf76647b8bfab9c6efee1f85fee6d81c7c568d Mon Sep 17 00:00:00 2001 From: Ashley Nelson Date: Fri, 14 Oct 2022 15:56:22 -0500 Subject: [PATCH 1/4] Update milestone counters when issue is deleted --- services/issue/issue.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/issue/issue.go b/services/issue/issue.go index bbd027879271d..4206d150a3720 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -224,6 +224,11 @@ func deleteIssue(issue *issues_model.Issue) error { return err } + if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { + return fmt.Errorf("error updating counters for milestone id %d: %w", + issue.MilestoneID, err) + } + if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil { return err } From 0f666a5f6771c4d5f3709eb85446deed507595a2 Mon Sep 17 00:00:00 2001 From: Ashley Nelson Date: Sat, 15 Oct 2022 16:32:05 +0000 Subject: [PATCH 2/4] Add migration to update all open milestones --- .../expected_milestone.yml | 19 ++++++++ .../Test_updateOpenMilestoneCounts/issue.yml | 21 +++++++++ .../milestone.yml | 19 ++++++++ models/migrations/migrations.go | 2 + models/migrations/v227.go | 46 +++++++++++++++++++ models/migrations/v227_test.go | 45 ++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml create mode 100644 models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml create mode 100644 models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml create mode 100644 models/migrations/v227.go create mode 100644 models/migrations/v227_test.go diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml new file mode 100644 index 0000000000000..9326fa550b148 --- /dev/null +++ b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml @@ -0,0 +1,19 @@ +# type Milestone struct { +# ID int64 `xorm:"pk autoincr"` +# IsClosed bool +# NumIssues int +# NumClosedIssues int +# Completeness int // Percentage(1-100). +# } +- + id: 1 + is_closed: false + num_issues: 3 + num_closed_issues: 1 + completeness: 33 +- + id: 2 + is_closed: true + num_issues: 5 + num_closed_issues: 5 + completeness: 100 diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml new file mode 100644 index 0000000000000..65e39da25ab19 --- /dev/null +++ b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml @@ -0,0 +1,21 @@ +# type Issue struct { +# ID int64 `xorm:"pk autoincr"` +# RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"` +# MilestoneID int64 `xorm:"INDEX"` +# IsClosed bool `xorm:"INDEX"` +# } +- + id: 1 + repo_id: 1 + milestone_id: 1 + is_closed: false +- + id: 2 + repo_id: 1 + milestone_id: 1 + is_closed: true +- + id: 4 + repo_id: 1 + milestone_id: 1 + is_closed: false diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml new file mode 100644 index 0000000000000..0bcf4cffd3aae --- /dev/null +++ b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml @@ -0,0 +1,19 @@ +# type Milestone struct { +# ID int64 `xorm:"pk autoincr"` +# IsClosed bool +# NumIssues int +# NumClosedIssues int +# Completeness int // Percentage(1-100). +# } +- + id: 1 + is_closed: false + num_issues: 4 + num_closed_issues: 2 + completeness: 50 +- + id: 2 + is_closed: true + num_issues: 5 + num_closed_issues: 5 + completeness: 100 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 2a38772180457..65ae3a380b9f4 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -415,6 +415,8 @@ var migrations = []Migration{ NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText), // v226 -> v227 NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField), + // v227 -> v228 + NewMigration("Update counts of all open milestones", updateOpenMilestoneCounts), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v227.go b/models/migrations/v227.go new file mode 100644 index 0000000000000..a469c7ecc981f --- /dev/null +++ b/models/migrations/v227.go @@ -0,0 +1,46 @@ +// Copyright 2022 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 migrations + +import ( + "fmt" + + "code.gitea.io/gitea/models/issues" + "xorm.io/builder" + "xorm.io/xorm" +) + +func updateOpenMilestoneCounts(x *xorm.Engine) error { + var openMilestoneIDs []int64 + err := x.Table("milestone").Select("id").Where(builder.Neq{"is_closed": 1}).Find(&openMilestoneIDs) + if err != nil { + return fmt.Errorf("error selecting open milestone IDs: %w", err) + } + + for _, id := range openMilestoneIDs { + _, err := x.ID(id). + SetExpr("num_issues", builder.Select("count(*)").From("issue").Where( + builder.Eq{"milestone_id": id}, + )). + SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where( + builder.Eq{ + "milestone_id": id, + "is_closed": true, + }, + )). + Update(&issues.Milestone{}) + if err != nil { + return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err) + } + _, err = x.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?", + id, + ) + if err != nil { + return fmt.Errorf("error setting completeness on milestone %d: %w", id, err) + } + } + + return nil +} diff --git a/models/migrations/v227_test.go b/models/migrations/v227_test.go new file mode 100644 index 0000000000000..794b7f8254969 --- /dev/null +++ b/models/migrations/v227_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 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 migrations + +import ( + "testing" + + "code.gitea.io/gitea/models/issues" + "github.com/stretchr/testify/assert" +) + +func Test_updateOpenMilestoneCounts(t *testing.T) { + type ExpectedMilestone issues.Milestone + + // Prepare and load the testing database + x, deferable := prepareTestEnv(t, 0, new(issues.Milestone), new(ExpectedMilestone), new(issues.Issue)) + defer deferable() + if x == nil || t.Failed() { + return + } + + if err := updateOpenMilestoneCounts(x); err != nil { + assert.NoError(t, err) + return + } + + expected := []ExpectedMilestone{} + if err := x.Table("expected_milestone").Asc("id").Find(&expected); !assert.NoError(t, err) { + return + } + + got := []issues.Milestone{} + if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) { + return + } + + for i, e := range expected { + got := got[i] + assert.Equal(t, e.ID, got.ID) + assert.Equal(t, e.NumIssues, got.NumIssues) + assert.Equal(t, e.NumClosedIssues, got.NumClosedIssues) + } +} From 85003cec527be90343ba9ab733bf8d5fd99ffc75 Mon Sep 17 00:00:00 2001 From: Ashley Nelson Date: Sun, 16 Oct 2022 16:36:16 +0000 Subject: [PATCH 3/4] Add index to issue fixtures so unique index constraint not violated --- .../fixtures/Test_updateOpenMilestoneCounts/issue.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml index 65e39da25ab19..fdaacd9f68270 100644 --- a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml +++ b/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml @@ -1,21 +1,25 @@ # type Issue struct { # ID int64 `xorm:"pk autoincr"` # RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"` +# Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository. # MilestoneID int64 `xorm:"INDEX"` # IsClosed bool `xorm:"INDEX"` # } - id: 1 repo_id: 1 + index: 1 milestone_id: 1 is_closed: false - id: 2 repo_id: 1 + index: 2 milestone_id: 1 is_closed: true - id: 4 repo_id: 1 + index: 3 milestone_id: 1 is_closed: false From e3795429a905a496c93d5d80bdf33bc7f80175be Mon Sep 17 00:00:00 2001 From: Ashley Nelson Date: Sun, 16 Oct 2022 17:31:13 +0000 Subject: [PATCH 4/4] Modify import formatting --- models/migrations/v227.go | 1 + models/migrations/v227_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/models/migrations/v227.go b/models/migrations/v227.go index a469c7ecc981f..42ec2033fee2f 100644 --- a/models/migrations/v227.go +++ b/models/migrations/v227.go @@ -8,6 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/issues" + "xorm.io/builder" "xorm.io/xorm" ) diff --git a/models/migrations/v227_test.go b/models/migrations/v227_test.go index 794b7f8254969..f8a147c9bd69a 100644 --- a/models/migrations/v227_test.go +++ b/models/migrations/v227_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/issues" + "github.com/stretchr/testify/assert" )