mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-09 17:37:20 -06:00
refactor: clean up comment model (#39779)
Try to figure out whether we can reuse the "comment" table for #36862 The legacy "CommitID" field is removed, and updated the comment. Some useless and outdated "doctor fixes" are also removed together.
This commit is contained in:
@@ -65,7 +65,7 @@ type CommentType int
|
|||||||
const CommentTypeUndefined CommentType = -1
|
const CommentTypeUndefined CommentType = -1
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CommentTypeComment CommentType = iota // 0 Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
|
CommentTypeComment CommentType = iota // 0 Plain comment, can be associated with "commit_sha" and a non-zero "line"
|
||||||
|
|
||||||
CommentTypeReopen // 1
|
CommentTypeReopen // 1
|
||||||
CommentTypeClose // 2
|
CommentTypeClose // 2
|
||||||
@@ -253,46 +253,57 @@ type CommentMetaData struct {
|
|||||||
|
|
||||||
// Comment represents a comment in commit and issue page.
|
// Comment represents a comment in commit and issue page.
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Type CommentType `xorm:"INDEX"`
|
Type CommentType
|
||||||
PosterID int64 `xorm:"INDEX"`
|
|
||||||
Poster *user_model.User `xorm:"-"`
|
PosterID int64 `xorm:"INDEX"`
|
||||||
|
Poster *user_model.User `xorm:"-"`
|
||||||
|
|
||||||
OriginalAuthor string
|
OriginalAuthor string
|
||||||
OriginalAuthorID int64
|
OriginalAuthorID int64
|
||||||
IssueID int64 `xorm:"INDEX"`
|
|
||||||
Issue *Issue `xorm:"-"`
|
IssueID int64 `xorm:"INDEX"`
|
||||||
LabelID int64
|
Issue *Issue `xorm:"-"`
|
||||||
Label *Label `xorm:"-"`
|
|
||||||
AddedLabels []*Label `xorm:"-"`
|
LabelID int64
|
||||||
RemovedLabels []*Label `xorm:"-"`
|
Label *Label `xorm:"-"`
|
||||||
OldProjectID int64
|
AddedLabels []*Label `xorm:"-"`
|
||||||
ProjectID int64
|
RemovedLabels []*Label `xorm:"-"`
|
||||||
OldProject *project_model.Project `xorm:"-"`
|
|
||||||
Project *project_model.Project `xorm:"-"`
|
OldProjectID int64
|
||||||
OldMilestoneID int64
|
ProjectID int64
|
||||||
MilestoneID int64
|
OldProject *project_model.Project `xorm:"-"`
|
||||||
OldMilestone *Milestone `xorm:"-"`
|
Project *project_model.Project `xorm:"-"`
|
||||||
Milestone *Milestone `xorm:"-"`
|
|
||||||
TimeID int64
|
OldMilestoneID int64
|
||||||
Time *TrackedTime `xorm:"-"`
|
MilestoneID int64
|
||||||
AssigneeID int64
|
OldMilestone *Milestone `xorm:"-"`
|
||||||
RemovedAssignee bool
|
Milestone *Milestone `xorm:"-"`
|
||||||
Assignee *user_model.User `xorm:"-"`
|
|
||||||
AssigneeTeamID int64 `xorm:"NOT NULL DEFAULT 0"`
|
TimeID int64
|
||||||
AssigneeTeam *organization.Team `xorm:"-"`
|
Time *TrackedTime `xorm:"-"`
|
||||||
ResolveDoerID int64
|
|
||||||
ResolveDoer *user_model.User `xorm:"-"`
|
AssigneeID int64
|
||||||
OldTitle string
|
RemovedAssignee bool
|
||||||
NewTitle string
|
Assignee *user_model.User `xorm:"-"`
|
||||||
OldRef string
|
AssigneeTeamID int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||||
NewRef string
|
AssigneeTeam *organization.Team `xorm:"-"`
|
||||||
|
|
||||||
|
ResolveDoerID int64
|
||||||
|
ResolveDoer *user_model.User `xorm:"-"`
|
||||||
|
|
||||||
|
OldTitle string
|
||||||
|
NewTitle string
|
||||||
|
OldRef string
|
||||||
|
NewRef string
|
||||||
|
|
||||||
DependentIssueID int64 `xorm:"index"` // This is used by issue_service.deleteIssue
|
DependentIssueID int64 `xorm:"index"` // This is used by issue_service.deleteIssue
|
||||||
DependentIssue *Issue `xorm:"-"`
|
DependentIssue *Issue `xorm:"-"`
|
||||||
|
|
||||||
CommitID int64
|
Line int64 // - previous line / + proposed line
|
||||||
Line int64 // - previous line / + proposed line
|
TreePath string `xorm:"VARCHAR(4000)"` // SQLServer only supports up to 4000
|
||||||
TreePath string `xorm:"VARCHAR(4000)"` // SQLServer only supports up to 4000
|
Content string `xorm:"LONGTEXT"`
|
||||||
Content string `xorm:"LONGTEXT"`
|
|
||||||
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
||||||
RenderedContent template.HTML `xorm:"-"`
|
RenderedContent template.HTML `xorm:"-"`
|
||||||
|
|
||||||
@@ -324,12 +335,12 @@ type Comment struct {
|
|||||||
RefAction references.XRefAction `xorm:"SMALLINT"` // What happens if RefIssueID resolves
|
RefAction references.XRefAction `xorm:"SMALLINT"` // What happens if RefIssueID resolves
|
||||||
RefIsPull bool
|
RefIsPull bool
|
||||||
|
|
||||||
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
|
|
||||||
|
|
||||||
RefRepo *repo_model.Repository `xorm:"-"`
|
RefRepo *repo_model.Repository `xorm:"-"`
|
||||||
RefIssue *Issue `xorm:"-"`
|
RefIssue *Issue `xorm:"-"`
|
||||||
RefComment *Comment `xorm:"-"`
|
RefComment *Comment `xorm:"-"`
|
||||||
|
|
||||||
|
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
|
||||||
|
|
||||||
Commits []*git_model.SignCommitWithStatuses `xorm:"-"`
|
Commits []*git_model.SignCommitWithStatuses `xorm:"-"`
|
||||||
OldCommit string `xorm:"-"`
|
OldCommit string `xorm:"-"`
|
||||||
NewCommit string `xorm:"-"`
|
NewCommit string `xorm:"-"`
|
||||||
@@ -849,7 +860,6 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
|
|||||||
RemovedAssignee: opts.RemovedAssignee,
|
RemovedAssignee: opts.RemovedAssignee,
|
||||||
AssigneeID: opts.AssigneeID,
|
AssigneeID: opts.AssigneeID,
|
||||||
AssigneeTeamID: opts.AssigneeTeamID,
|
AssigneeTeamID: opts.AssigneeTeamID,
|
||||||
CommitID: opts.CommitID,
|
|
||||||
CommitSHA: opts.CommitSHA,
|
CommitSHA: opts.CommitSHA,
|
||||||
Line: opts.LineNum,
|
Line: opts.LineNum,
|
||||||
Content: opts.Content,
|
Content: opts.Content,
|
||||||
@@ -1015,7 +1025,6 @@ type CreateCommentOptions struct {
|
|||||||
NewTitle string
|
NewTitle string
|
||||||
OldRef string
|
OldRef string
|
||||||
NewRef string
|
NewRef string
|
||||||
CommitID int64
|
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
Patch string
|
Patch string
|
||||||
LineNum int64
|
LineNum int64
|
||||||
@@ -1258,45 +1267,6 @@ func (c *Comment) GetExternalName() string { return c.OriginalAuthor }
|
|||||||
// GetExternalID ExternalUserRemappable interface
|
// GetExternalID ExternalUserRemappable interface
|
||||||
func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID }
|
func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID }
|
||||||
|
|
||||||
// CountCommentTypeLabelWithEmptyLabel count label comments with empty label
|
|
||||||
func CountCommentTypeLabelWithEmptyLabel(ctx context.Context) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixCommentTypeLabelWithEmptyLabel count label comments with empty label
|
|
||||||
func FixCommentTypeLabelWithEmptyLabel(ctx context.Context) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
|
|
||||||
func CountCommentTypeLabelWithOutsideLabels(ctx context.Context) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
|
|
||||||
Table("comment").
|
|
||||||
Join("inner", "label", "label.id = comment.label_id").
|
|
||||||
Join("inner", "issue", "issue.id = comment.issue_id ").
|
|
||||||
Join("inner", "repository", "issue.repo_id = repository.id").
|
|
||||||
Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixCommentTypeLabelWithOutsideLabels count label comments with outside label
|
|
||||||
func FixCommentTypeLabelWithOutsideLabels(ctx context.Context) (int64, error) {
|
|
||||||
res, err := db.GetEngine(ctx).Exec(`DELETE FROM comment WHERE comment.id IN (
|
|
||||||
SELECT il_too.id FROM (
|
|
||||||
SELECT com.id
|
|
||||||
FROM comment AS com
|
|
||||||
INNER JOIN label ON com.label_id = label.id
|
|
||||||
INNER JOIN issue on issue.id = com.issue_id
|
|
||||||
INNER JOIN repository ON issue.repo_id = repository.id
|
|
||||||
WHERE
|
|
||||||
com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
|
|
||||||
) AS il_too)`, CommentTypeLabel)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.RowsAffected()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasOriginalAuthor returns if a comment was migrated and has an original author.
|
// HasOriginalAuthor returns if a comment was migrated and has an original author.
|
||||||
func (c *Comment) HasOriginalAuthor() bool {
|
func (c *Comment) HasOriginalAuthor() bool {
|
||||||
return c.OriginalAuthor != "" && c.OriginalAuthorID != 0
|
return c.OriginalAuthor != "" && c.OriginalAuthorID != 0
|
||||||
|
|||||||
@@ -282,35 +282,6 @@ func DeleteOrphanedIssueLabels(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountIssueLabelWithOutsideLabels count label comments with outside label
|
|
||||||
func CountIssueLabelWithOutsideLabels(ctx context.Context) (int64, error) {
|
|
||||||
return db.GetEngine(ctx).Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
|
|
||||||
Table("issue_label").
|
|
||||||
Join("inner", "label", "issue_label.label_id = label.id ").
|
|
||||||
Join("inner", "issue", "issue.id = issue_label.issue_id ").
|
|
||||||
Join("inner", "repository", "issue.repo_id = repository.id").
|
|
||||||
Count(new(IssueLabel))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixIssueLabelWithOutsideLabels fix label comments with outside label
|
|
||||||
func FixIssueLabelWithOutsideLabels(ctx context.Context) (int64, error) {
|
|
||||||
res, err := db.GetEngine(ctx).Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
|
|
||||||
SELECT il_too.id FROM (
|
|
||||||
SELECT il_too_too.id
|
|
||||||
FROM issue_label AS il_too_too
|
|
||||||
INNER JOIN label ON il_too_too.label_id = label.id
|
|
||||||
INNER JOIN issue on issue.id = il_too_too.issue_id
|
|
||||||
INNER JOIN repository on repository.id = issue.repo_id
|
|
||||||
WHERE
|
|
||||||
(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
|
|
||||||
) AS il_too )`)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.RowsAffected()
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadLabels loads labels
|
// LoadLabels loads labels
|
||||||
func (issue *Issue) LoadLabels(ctx context.Context) (err error) {
|
func (issue *Issue) LoadLabels(ctx context.Context) (err error) {
|
||||||
if !issue.isLabelsLoaded && issue.Labels == nil && issue.ID != 0 {
|
if !issue.isLabelsLoaded && issue.Labels == nil && issue.ID != 0 {
|
||||||
|
|||||||
@@ -257,15 +257,17 @@ func DeleteAttachmentsByRelease(ctx context.Context, releaseID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountOrphanedAttachments returns the number of bad attachments
|
func sqlOrphanedAttachmentsWhereCond() string {
|
||||||
func CountOrphanedAttachments(ctx context.Context) (int64, error) {
|
return "(issue_id > 0 and issue_id not in (select id from `issue`))" +
|
||||||
return db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
|
" or (release_id > 0 and release_id not in (select id from `release`))" +
|
||||||
Count(new(Attachment))
|
" or (comment_id > 0 and comment_id not in (select id from `comment`))"
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountOrphanedAttachments(ctx context.Context) (int64, error) {
|
||||||
|
return db.GetEngine(ctx).Where(sqlOrphanedAttachmentsWhereCond()).Count(new(Attachment))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOrphanedAttachments delete all bad attachments
|
|
||||||
func DeleteOrphanedAttachments(ctx context.Context) error {
|
func DeleteOrphanedAttachments(ctx context.Context) error {
|
||||||
_, err := db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
|
_, err := db.GetEngine(ctx).Where(sqlOrphanedAttachmentsWhereCond()).Delete(new(Attachment))
|
||||||
Delete(new(Attachment))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ package repo_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dev/models/db"
|
||||||
|
issues_model "gitea.dev/models/issues"
|
||||||
repo_model "gitea.dev/models/repo"
|
repo_model "gitea.dev/models/repo"
|
||||||
"gitea.dev/models/unittest"
|
"gitea.dev/models/unittest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIncreaseDownloadCount(t *testing.T) {
|
func TestIncreaseDownloadCount(t *testing.T) {
|
||||||
@@ -69,6 +72,34 @@ func TestGetAttachmentByID(t *testing.T) {
|
|||||||
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteOrphanedAttachments(t *testing.T) {
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
issue := &issues_model.Issue{}
|
||||||
|
release := &repo_model.Release{}
|
||||||
|
comment := &issues_model.Comment{}
|
||||||
|
require.NoError(t, db.Insert(t.Context(), issue, release, comment))
|
||||||
|
require.NoError(t, db.Insert(t.Context(), []*repo_model.Attachment{
|
||||||
|
{UUID: "valid-issue", IssueID: issue.ID},
|
||||||
|
{UUID: "valid-release", ReleaseID: release.ID},
|
||||||
|
{UUID: "valid-comment", CommentID: comment.ID},
|
||||||
|
{UUID: "unlinked"},
|
||||||
|
{UUID: "orphaned-issue", IssueID: unittest.NonexistentID},
|
||||||
|
{UUID: "orphaned-release", ReleaseID: unittest.NonexistentID},
|
||||||
|
{UUID: "orphaned-comment", IssueID: issue.ID, CommentID: unittest.NonexistentID},
|
||||||
|
}))
|
||||||
|
|
||||||
|
require.NoError(t, repo_model.DeleteOrphanedAttachments(t.Context()))
|
||||||
|
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{UUID: "valid-issue"})
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{UUID: "valid-release"})
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{UUID: "valid-comment"})
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &repo_model.Attachment{UUID: "unlinked"})
|
||||||
|
unittest.AssertNotExistsBean(t, &repo_model.Attachment{UUID: "orphaned-issue"})
|
||||||
|
unittest.AssertNotExistsBean(t, &repo_model.Attachment{UUID: "orphaned-release"})
|
||||||
|
unittest.AssertNotExistsBean(t, &repo_model.Attachment{UUID: "orphaned-comment"})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAttachment_DownloadURL(t *testing.T) {
|
func TestAttachment_DownloadURL(t *testing.T) {
|
||||||
attach := &repo_model.Attachment{
|
attach := &repo_model.Attachment{
|
||||||
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||||
|
|||||||
@@ -108,9 +108,8 @@ func prepareDBConsistencyChecks() []consistencyCheck {
|
|||||||
// find tracked times without existing issues/pulls
|
// find tracked times without existing issues/pulls
|
||||||
genericOrphanCheck("Orphaned TrackedTimes without existing issue",
|
genericOrphanCheck("Orphaned TrackedTimes without existing issue",
|
||||||
"tracked_time", "issue", "tracked_time.issue_id=issue.id"),
|
"tracked_time", "issue", "tracked_time.issue_id=issue.id"),
|
||||||
// find attachments without existing issues or releases
|
|
||||||
{
|
{
|
||||||
Name: "Orphaned Attachments without existing issues or releases",
|
Name: "Orphaned Attachments without existing issues, comments or releases",
|
||||||
Counter: repo_model.CountOrphanedAttachments,
|
Counter: repo_model.CountOrphanedAttachments,
|
||||||
Fixer: asFixer(repo_model.DeleteOrphanedAttachments),
|
Fixer: asFixer(repo_model.DeleteOrphanedAttachments),
|
||||||
},
|
},
|
||||||
@@ -121,27 +120,6 @@ func prepareDBConsistencyChecks() []consistencyCheck {
|
|||||||
Fixer: repo_model.FixNullArchivedRepository,
|
Fixer: repo_model.FixNullArchivedRepository,
|
||||||
FixedMessage: "Fixed",
|
FixedMessage: "Fixed",
|
||||||
},
|
},
|
||||||
// find label comments with empty labels
|
|
||||||
{
|
|
||||||
Name: "Label comments with empty labels",
|
|
||||||
Counter: issues_model.CountCommentTypeLabelWithEmptyLabel,
|
|
||||||
Fixer: issues_model.FixCommentTypeLabelWithEmptyLabel,
|
|
||||||
FixedMessage: "Fixed",
|
|
||||||
},
|
|
||||||
// find label comments with labels from outside the repository
|
|
||||||
{
|
|
||||||
Name: "Label comments with labels from outside the repository",
|
|
||||||
Counter: issues_model.CountCommentTypeLabelWithOutsideLabels,
|
|
||||||
Fixer: issues_model.FixCommentTypeLabelWithOutsideLabels,
|
|
||||||
FixedMessage: "Removed",
|
|
||||||
},
|
|
||||||
// find issue_label with labels from outside the repository
|
|
||||||
{
|
|
||||||
Name: "IssueLabels with Labels from outside the repository",
|
|
||||||
Counter: issues_model.CountIssueLabelWithOutsideLabels,
|
|
||||||
Fixer: issues_model.FixIssueLabelWithOutsideLabels,
|
|
||||||
FixedMessage: "Removed",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "Action with created_unix set as an empty string",
|
Name: "Action with created_unix set as an empty string",
|
||||||
Counter: activities_model.CountActionCreatedUnixString,
|
Counter: activities_model.CountActionCreatedUnixString,
|
||||||
|
|||||||
Reference in New Issue
Block a user