Skip to content

Commit 20bb80b

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: [API] Allow removing issues (go-gitea#18879) Refactor SecToTime() function (go-gitea#18863) Improve mirror iterator (go-gitea#18928) Fix login with email panic when email is not exist (go-gitea#18941)
2 parents a88f5ef + 062fd4c commit 20bb80b

19 files changed

+383
-64
lines changed

models/issue.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"strconv"
1414
"strings"
1515

16+
admin_model "code.gitea.io/gitea/models/admin"
1617
"code.gitea.io/gitea/models/db"
1718
"code.gitea.io/gitea/models/issues"
1819
"code.gitea.io/gitea/models/perm"
@@ -24,6 +25,7 @@ import (
2425
"code.gitea.io/gitea/modules/log"
2526
"code.gitea.io/gitea/modules/references"
2627
"code.gitea.io/gitea/modules/setting"
28+
"code.gitea.io/gitea/modules/storage"
2729
api "code.gitea.io/gitea/modules/structs"
2830
"code.gitea.io/gitea/modules/timeutil"
2931
"code.gitea.io/gitea/modules/util"
@@ -1990,6 +1992,118 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
19901992
return committer.Commit()
19911993
}
19921994

1995+
// DeleteIssue deletes the issue
1996+
func DeleteIssue(issue *Issue) error {
1997+
ctx, committer, err := db.TxContext()
1998+
if err != nil {
1999+
return err
2000+
}
2001+
defer committer.Close()
2002+
2003+
if err := deleteIssue(ctx, issue); err != nil {
2004+
return err
2005+
}
2006+
2007+
return committer.Commit()
2008+
}
2009+
2010+
func deleteInIssue(e db.Engine, issueID int64, beans ...interface{}) error {
2011+
for _, bean := range beans {
2012+
if _, err := e.In("issue_id", issueID).Delete(bean); err != nil {
2013+
return err
2014+
}
2015+
}
2016+
return nil
2017+
}
2018+
2019+
func deleteIssue(ctx context.Context, issue *Issue) error {
2020+
e := db.GetEngine(ctx)
2021+
if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
2022+
return err
2023+
}
2024+
2025+
if issue.IsPull {
2026+
if _, err := e.ID(issue.RepoID).Decr("num_pulls").Update(new(repo_model.Repository)); err != nil {
2027+
return err
2028+
}
2029+
if issue.IsClosed {
2030+
if _, err := e.ID(issue.RepoID).Decr("num_closed_pulls").Update(new(repo_model.Repository)); err != nil {
2031+
return err
2032+
}
2033+
}
2034+
} else {
2035+
if _, err := e.ID(issue.RepoID).Decr("num_issues").Update(new(repo_model.Repository)); err != nil {
2036+
return err
2037+
}
2038+
if issue.IsClosed {
2039+
if _, err := e.ID(issue.RepoID).Decr("num_closed_issues").Update(new(repo_model.Repository)); err != nil {
2040+
return err
2041+
}
2042+
}
2043+
}
2044+
2045+
// delete actions assigned to this issue
2046+
var comments []int64
2047+
if err := e.Table(new(Comment)).In("issue_id", issue.ID).Cols("id").Find(&comments); err != nil {
2048+
return err
2049+
}
2050+
for i := range comments {
2051+
if _, err := e.Where("comment_id = ?", comments[i]).Delete(&Action{}); err != nil {
2052+
return err
2053+
}
2054+
}
2055+
if _, err := e.Table("action").Where("repo_id = ?", issue.RepoID).In("op_type", ActionCreateIssue, ActionCreatePullRequest).
2056+
Where("content LIKE ?", strconv.FormatInt(issue.ID, 10)+"|%").Delete(&Action{}); err != nil {
2057+
return err
2058+
}
2059+
2060+
// find attachments related to this issue and remove them
2061+
var attachments []*repo_model.Attachment
2062+
if err := e.In("issue_id", issue.ID).Find(&attachments); err != nil {
2063+
return err
2064+
}
2065+
2066+
for i := range attachments {
2067+
admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachments[i].RelativePath())
2068+
}
2069+
2070+
// delete all database data still assigned to this issue
2071+
if err := deleteInIssue(e, issue.ID,
2072+
&issues.ContentHistory{},
2073+
&Comment{},
2074+
&IssueLabel{},
2075+
&IssueDependency{},
2076+
&IssueAssignees{},
2077+
&IssueUser{},
2078+
&Reaction{},
2079+
&IssueWatch{},
2080+
&Stopwatch{},
2081+
&TrackedTime{},
2082+
&ProjectIssue{},
2083+
&repo_model.Attachment{},
2084+
&PullRequest{},
2085+
); err != nil {
2086+
return err
2087+
}
2088+
2089+
// References to this issue in other issues
2090+
if _, err := e.In("ref_issue_id", issue.ID).Delete(&Comment{}); err != nil {
2091+
return err
2092+
}
2093+
2094+
// Delete dependencies for issues in other repositories
2095+
if _, err := e.In("dependency_id", issue.ID).Delete(&IssueDependency{}); err != nil {
2096+
return err
2097+
}
2098+
2099+
// delete from dependent issues
2100+
if _, err := e.In("dependent_issue_id", issue.ID).Delete(&Comment{}); err != nil {
2101+
return err
2102+
}
2103+
2104+
return nil
2105+
}
2106+
19932107
// DependencyInfo represents high level information about an issue which is a dependency of another issue.
19942108
type DependencyInfo struct {
19952109
Issue `xorm:"extends"`

models/issue_comment.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,9 +1152,7 @@ func DeleteComment(comment *Comment) error {
11521152
}
11531153

11541154
func deleteComment(e db.Engine, comment *Comment) error {
1155-
if _, err := e.Delete(&Comment{
1156-
ID: comment.ID,
1157-
}); err != nil {
1155+
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
11581156
return err
11591157
}
11601158

models/issue_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,58 @@ func TestIssue_InsertIssue(t *testing.T) {
397397
assert.NoError(t, err)
398398
}
399399

400+
func TestIssue_DeleteIssue(t *testing.T) {
401+
assert.NoError(t, unittest.PrepareTestDatabase())
402+
403+
issueIDs, err := GetIssueIDsByRepoID(1)
404+
assert.NoError(t, err)
405+
assert.EqualValues(t, 5, len(issueIDs))
406+
407+
issue := &Issue{
408+
RepoID: 1,
409+
ID: issueIDs[2],
410+
}
411+
412+
err = DeleteIssue(issue)
413+
assert.NoError(t, err)
414+
issueIDs, err = GetIssueIDsByRepoID(1)
415+
assert.NoError(t, err)
416+
assert.EqualValues(t, 4, len(issueIDs))
417+
418+
// check attachment removal
419+
attachments, err := repo_model.GetAttachmentsByIssueID(4)
420+
assert.NoError(t, err)
421+
issue, err = GetIssueByID(4)
422+
assert.NoError(t, err)
423+
err = DeleteIssue(issue)
424+
assert.NoError(t, err)
425+
assert.EqualValues(t, 2, len(attachments))
426+
for i := range attachments {
427+
attachment, err := repo_model.GetAttachmentByUUID(attachments[i].UUID)
428+
assert.Error(t, err)
429+
assert.True(t, repo_model.IsErrAttachmentNotExist(err))
430+
assert.Nil(t, attachment)
431+
}
432+
433+
// check issue dependencies
434+
user, err := user_model.GetUserByID(1)
435+
assert.NoError(t, err)
436+
issue1, err := GetIssueByID(1)
437+
assert.NoError(t, err)
438+
issue2, err := GetIssueByID(2)
439+
assert.NoError(t, err)
440+
err = CreateIssueDependency(user, issue1, issue2)
441+
assert.NoError(t, err)
442+
left, err := IssueNoDependenciesLeft(issue1)
443+
assert.NoError(t, err)
444+
assert.False(t, left)
445+
err = DeleteIssue(&Issue{ID: 2})
446+
assert.NoError(t, err)
447+
left, err = IssueNoDependenciesLeft(issue1)
448+
assert.NoError(t, err)
449+
assert.True(t, left)
450+
}
451+
400452
func TestIssue_ResolveMentions(t *testing.T) {
401453
assert.NoError(t, unittest.PrepareTestDatabase())
402454

models/issue_tracked_time_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestAddTime(t *testing.T) {
3434
assert.Equal(t, int64(3661), tt.Time)
3535

3636
comment := unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*Comment)
37-
assert.Equal(t, comment.Content, "1h 1m 1s")
37+
assert.Equal(t, comment.Content, "1 hour 1 minute")
3838
}
3939

4040
func TestGetTrackedTimes(t *testing.T) {
@@ -86,17 +86,17 @@ func TestTotalTimes(t *testing.T) {
8686
assert.Len(t, total, 1)
8787
for user, time := range total {
8888
assert.Equal(t, int64(1), user.ID)
89-
assert.Equal(t, "6m 40s", time)
89+
assert.Equal(t, "6 minutes 40 seconds", time)
9090
}
9191

9292
total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2})
9393
assert.NoError(t, err)
9494
assert.Len(t, total, 2)
9595
for user, time := range total {
9696
if user.ID == 2 {
97-
assert.Equal(t, "1h 1m 2s", time)
97+
assert.Equal(t, "1 hour 1 minute", time)
9898
} else if user.ID == 1 {
99-
assert.Equal(t, "20s", time)
99+
assert.Equal(t, "20 seconds", time)
100100
} else {
101101
assert.Error(t, assert.AnError)
102102
}
@@ -107,7 +107,7 @@ func TestTotalTimes(t *testing.T) {
107107
assert.Len(t, total, 1)
108108
for user, time := range total {
109109
assert.Equal(t, int64(2), user.ID)
110-
assert.Equal(t, "1s", time)
110+
assert.Equal(t, "1 second", time)
111111
}
112112

113113
total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4})

models/repo/mirror.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,12 @@ func DeleteMirrorByRepoID(repoID int64) error {
120120
}
121121

122122
// MirrorsIterate iterates all mirror repositories.
123-
func MirrorsIterate(f func(idx int, bean interface{}) error) error {
123+
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
124124
return db.GetEngine(db.DefaultContext).
125125
Where("next_update_unix<=?", time.Now().Unix()).
126126
And("next_update_unix!=0").
127127
OrderBy("updated_unix ASC").
128+
Limit(limit).
128129
Iterate(new(Mirror), f)
129130
}
130131

models/repo/pushmirror.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,11 @@ func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) {
101101
}
102102

103103
// PushMirrorsIterate iterates all push-mirror repositories.
104-
func PushMirrorsIterate(f func(idx int, bean interface{}) error) error {
104+
func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
105105
return db.GetEngine(db.DefaultContext).
106106
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
107107
And("`interval` != 0").
108108
OrderBy("last_update ASC").
109+
Limit(limit).
109110
Iterate(new(PushMirror), f)
110111
}

models/repo/pushmirror_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func TestPushMirrorsIterate(t *testing.T) {
4040

4141
time.Sleep(1 * time.Millisecond)
4242

43-
PushMirrorsIterate(func(idx int, bean interface{}) error {
43+
PushMirrorsIterate(1, func(idx int, bean interface{}) error {
4444
m, ok := bean.(*PushMirror)
4545
assert.True(t, ok)
4646
assert.Equal(t, "test-1", m.RemoteName)

modules/nosql/manager_leveldb.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212

1313
"code.gitea.io/gitea/modules/log"
14+
1415
"github.com/syndtr/goleveldb/leveldb"
1516
"github.com/syndtr/goleveldb/leveldb/errors"
1617
"github.com/syndtr/goleveldb/leveldb/opt"

modules/notification/base/notifier.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Notifier interface {
2222
NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string)
2323
NotifyNewIssue(issue *models.Issue, mentions []*user_model.User)
2424
NotifyIssueChangeStatus(*user_model.User, *models.Issue, *models.Comment, bool)
25+
NotifyDeleteIssue(*user_model.User, *models.Issue)
2526
NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64)
2627
NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment)
2728
NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment)

modules/notification/base/null.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func (*NullNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.
3333
func (*NullNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
3434
}
3535

36+
// NotifyDeleteIssue notify when some issue deleted
37+
func (*NullNotifier) NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) {
38+
}
39+
3640
// NotifyNewPullRequest places a place holder function
3741
func (*NullNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
3842
}

modules/notification/notification.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionC
6060
}
6161
}
6262

63+
// NotifyDeleteIssue notify when some issue deleted
64+
func NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) {
65+
for _, notifier := range notifiers {
66+
notifier.NotifyDeleteIssue(doer, issue)
67+
}
68+
}
69+
6370
// NotifyMergePullRequest notifies merge pull request to notifiers
6471
func NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
6572
for _, notifier := range notifiers {

0 commit comments

Comments
 (0)