Skip to content

Commit

Permalink
Merge remote-tracking branch 'giteaofficial/main'
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
zjjhot committed Mar 1, 2022
2 parents a88f5ef + 062fd4c commit 20bb80b
Show file tree
Hide file tree
Showing 19 changed files with 383 additions and 64 deletions.
114 changes: 114 additions & 0 deletions models/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"strconv"
"strings"

admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm"
Expand All @@ -24,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
Expand Down Expand Up @@ -1990,6 +1992,118 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
return committer.Commit()
}

// DeleteIssue deletes the issue
func DeleteIssue(issue *Issue) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()

if err := deleteIssue(ctx, issue); err != nil {
return err
}

return committer.Commit()
}

func deleteInIssue(e db.Engine, issueID int64, beans ...interface{}) error {
for _, bean := range beans {
if _, err := e.In("issue_id", issueID).Delete(bean); err != nil {
return err
}
}
return nil
}

func deleteIssue(ctx context.Context, issue *Issue) error {
e := db.GetEngine(ctx)
if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil {
return err
}

if issue.IsPull {
if _, err := e.ID(issue.RepoID).Decr("num_pulls").Update(new(repo_model.Repository)); err != nil {
return err
}
if issue.IsClosed {
if _, err := e.ID(issue.RepoID).Decr("num_closed_pulls").Update(new(repo_model.Repository)); err != nil {
return err
}
}
} else {
if _, err := e.ID(issue.RepoID).Decr("num_issues").Update(new(repo_model.Repository)); err != nil {
return err
}
if issue.IsClosed {
if _, err := e.ID(issue.RepoID).Decr("num_closed_issues").Update(new(repo_model.Repository)); err != nil {
return err
}
}
}

// delete actions assigned to this issue
var comments []int64
if err := e.Table(new(Comment)).In("issue_id", issue.ID).Cols("id").Find(&comments); err != nil {
return err
}
for i := range comments {
if _, err := e.Where("comment_id = ?", comments[i]).Delete(&Action{}); err != nil {
return err
}
}
if _, err := e.Table("action").Where("repo_id = ?", issue.RepoID).In("op_type", ActionCreateIssue, ActionCreatePullRequest).
Where("content LIKE ?", strconv.FormatInt(issue.ID, 10)+"|%").Delete(&Action{}); err != nil {
return err
}

// find attachments related to this issue and remove them
var attachments []*repo_model.Attachment
if err := e.In("issue_id", issue.ID).Find(&attachments); err != nil {
return err
}

for i := range attachments {
admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachments[i].RelativePath())
}

// delete all database data still assigned to this issue
if err := deleteInIssue(e, issue.ID,
&issues.ContentHistory{},
&Comment{},
&IssueLabel{},
&IssueDependency{},
&IssueAssignees{},
&IssueUser{},
&Reaction{},
&IssueWatch{},
&Stopwatch{},
&TrackedTime{},
&ProjectIssue{},
&repo_model.Attachment{},
&PullRequest{},
); err != nil {
return err
}

// References to this issue in other issues
if _, err := e.In("ref_issue_id", issue.ID).Delete(&Comment{}); err != nil {
return err
}

// Delete dependencies for issues in other repositories
if _, err := e.In("dependency_id", issue.ID).Delete(&IssueDependency{}); err != nil {
return err
}

// delete from dependent issues
if _, err := e.In("dependent_issue_id", issue.ID).Delete(&Comment{}); err != nil {
return err
}

return nil
}

// DependencyInfo represents high level information about an issue which is a dependency of another issue.
type DependencyInfo struct {
Issue `xorm:"extends"`
Expand Down
4 changes: 1 addition & 3 deletions models/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,9 +1152,7 @@ func DeleteComment(comment *Comment) error {
}

func deleteComment(e db.Engine, comment *Comment) error {
if _, err := e.Delete(&Comment{
ID: comment.ID,
}); err != nil {
if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil {
return err
}

Expand Down
52 changes: 52 additions & 0 deletions models/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,58 @@ func TestIssue_InsertIssue(t *testing.T) {
assert.NoError(t, err)
}

func TestIssue_DeleteIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

issueIDs, err := GetIssueIDsByRepoID(1)
assert.NoError(t, err)
assert.EqualValues(t, 5, len(issueIDs))

issue := &Issue{
RepoID: 1,
ID: issueIDs[2],
}

err = DeleteIssue(issue)
assert.NoError(t, err)
issueIDs, err = GetIssueIDsByRepoID(1)
assert.NoError(t, err)
assert.EqualValues(t, 4, len(issueIDs))

// check attachment removal
attachments, err := repo_model.GetAttachmentsByIssueID(4)
assert.NoError(t, err)
issue, err = GetIssueByID(4)
assert.NoError(t, err)
err = DeleteIssue(issue)
assert.NoError(t, err)
assert.EqualValues(t, 2, len(attachments))
for i := range attachments {
attachment, err := repo_model.GetAttachmentByUUID(attachments[i].UUID)
assert.Error(t, err)
assert.True(t, repo_model.IsErrAttachmentNotExist(err))
assert.Nil(t, attachment)
}

// check issue dependencies
user, err := user_model.GetUserByID(1)
assert.NoError(t, err)
issue1, err := GetIssueByID(1)
assert.NoError(t, err)
issue2, err := GetIssueByID(2)
assert.NoError(t, err)
err = CreateIssueDependency(user, issue1, issue2)
assert.NoError(t, err)
left, err := IssueNoDependenciesLeft(issue1)
assert.NoError(t, err)
assert.False(t, left)
err = DeleteIssue(&Issue{ID: 2})
assert.NoError(t, err)
left, err = IssueNoDependenciesLeft(issue1)
assert.NoError(t, err)
assert.True(t, left)
}

func TestIssue_ResolveMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

Expand Down
10 changes: 5 additions & 5 deletions models/issue_tracked_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestAddTime(t *testing.T) {
assert.Equal(t, int64(3661), tt.Time)

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

func TestGetTrackedTimes(t *testing.T) {
Expand Down Expand Up @@ -86,17 +86,17 @@ func TestTotalTimes(t *testing.T) {
assert.Len(t, total, 1)
for user, time := range total {
assert.Equal(t, int64(1), user.ID)
assert.Equal(t, "6m 40s", time)
assert.Equal(t, "6 minutes 40 seconds", time)
}

total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2})
assert.NoError(t, err)
assert.Len(t, total, 2)
for user, time := range total {
if user.ID == 2 {
assert.Equal(t, "1h 1m 2s", time)
assert.Equal(t, "1 hour 1 minute", time)
} else if user.ID == 1 {
assert.Equal(t, "20s", time)
assert.Equal(t, "20 seconds", time)
} else {
assert.Error(t, assert.AnError)
}
Expand All @@ -107,7 +107,7 @@ func TestTotalTimes(t *testing.T) {
assert.Len(t, total, 1)
for user, time := range total {
assert.Equal(t, int64(2), user.ID)
assert.Equal(t, "1s", time)
assert.Equal(t, "1 second", time)
}

total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4})
Expand Down
3 changes: 2 additions & 1 deletion models/repo/mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,12 @@ func DeleteMirrorByRepoID(repoID int64) error {
}

// MirrorsIterate iterates all mirror repositories.
func MirrorsIterate(f func(idx int, bean interface{}) error) error {
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
return db.GetEngine(db.DefaultContext).
Where("next_update_unix<=?", time.Now().Unix()).
And("next_update_unix!=0").
OrderBy("updated_unix ASC").
Limit(limit).
Iterate(new(Mirror), f)
}

Expand Down
3 changes: 2 additions & 1 deletion models/repo/pushmirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) {
}

// PushMirrorsIterate iterates all push-mirror repositories.
func PushMirrorsIterate(f func(idx int, bean interface{}) error) error {
func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
return db.GetEngine(db.DefaultContext).
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
And("`interval` != 0").
OrderBy("last_update ASC").
Limit(limit).
Iterate(new(PushMirror), f)
}
2 changes: 1 addition & 1 deletion models/repo/pushmirror_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestPushMirrorsIterate(t *testing.T) {

time.Sleep(1 * time.Millisecond)

PushMirrorsIterate(func(idx int, bean interface{}) error {
PushMirrorsIterate(1, func(idx int, bean interface{}) error {
m, ok := bean.(*PushMirror)
assert.True(t, ok)
assert.Equal(t, "test-1", m.RemoteName)
Expand Down
1 change: 1 addition & 0 deletions modules/nosql/manager_leveldb.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"

"code.gitea.io/gitea/modules/log"

"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/opt"
Expand Down
1 change: 1 addition & 0 deletions modules/notification/base/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Notifier interface {
NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string)
NotifyNewIssue(issue *models.Issue, mentions []*user_model.User)
NotifyIssueChangeStatus(*user_model.User, *models.Issue, *models.Comment, bool)
NotifyDeleteIssue(*user_model.User, *models.Issue)
NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64)
NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment)
NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment)
Expand Down
4 changes: 4 additions & 0 deletions modules/notification/base/null.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ func (*NullNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.
func (*NullNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
}

// NotifyDeleteIssue notify when some issue deleted
func (*NullNotifier) NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) {
}

// NotifyNewPullRequest places a place holder function
func (*NullNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
}
Expand Down
7 changes: 7 additions & 0 deletions modules/notification/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ func NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionC
}
}

// NotifyDeleteIssue notify when some issue deleted
func NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) {
for _, notifier := range notifiers {
notifier.NotifyDeleteIssue(doer, issue)
}
}

// NotifyMergePullRequest notifies merge pull request to notifiers
func NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
for _, notifier := range notifiers {
Expand Down
Loading

0 comments on commit 20bb80b

Please sign in to comment.