[API] Allow removing issues (#18879)
Add new feature to delete issues and pulls via API Co-authored-by: fnetx <git@fralix.ovh> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Gusted <williamzijl7@hotmail.com> Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
6859b69198
commit
062fd4c217
11 changed files with 299 additions and 4 deletions
114
models/issue.go
114
models/issue.go
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue