Skip to content

Commit

Permalink
Migrate reactions when migrating repository from github (#9599)
Browse files Browse the repository at this point in the history
* Migrate reactions when migrating repository from github

* fix missed sleep

* fix tests

* update reactions when external user binding

* Fix test

* fix tests

* change the copy head

* fix test

* fix migrator add/delete reaction
  • Loading branch information
lunny authored and sapk committed Jan 15, 2020
1 parent 4e566df commit 2b3e931
Show file tree
Hide file tree
Showing 18 changed files with 329 additions and 101 deletions.
6 changes: 5 additions & 1 deletion models/external_login_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,9 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
return err
}

return UpdateReleasesMigrationsByType(tp, externalUserID, userID)
if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
return err
}

return UpdateReactionsMigrationsByType(tp, externalUserID, userID)
}
19 changes: 18 additions & 1 deletion models/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,11 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
if err != nil {
return err
}
if err = issue.loadRepo(e); err != nil {
return err
}
// Load reaction user data
if _, err := ReactionList(reactions).loadUsers(e); err != nil {
if _, err := ReactionList(reactions).loadUsers(e, issue.Repo); err != nil {
return err
}

Expand Down Expand Up @@ -1836,3 +1839,17 @@ func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, origina
})
return err
}

// UpdateReactionsMigrationsByType updates all migrated repositories' reactions from gitServiceType to replace originalAuthorID to posterID
func UpdateReactionsMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, userID int64) error {
_, err := x.Table("reaction").
Join("INNER", "issue", "issue.id = reaction.issue_id").
Where("issue.repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
And("reaction.original_author_id = ?", originalAuthorID).
Update(map[string]interface{}{
"user_id": userID,
"original_author": "",
"original_author_id": 0,
})
return err
}
8 changes: 4 additions & 4 deletions models/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func (c *Comment) LoadDepIssueDetails() (err error) {
return err
}

func (c *Comment) loadReactions(e Engine) (err error) {
func (c *Comment) loadReactions(e Engine, repo *Repository) (err error) {
if c.Reactions != nil {
return nil
}
Expand All @@ -437,15 +437,15 @@ func (c *Comment) loadReactions(e Engine) (err error) {
return err
}
// Load reaction user data
if _, err := c.Reactions.LoadUsers(); err != nil {
if _, err := c.Reactions.loadUsers(e, repo); err != nil {
return err
}
return nil
}

// LoadReactions loads comment reactions
func (c *Comment) LoadReactions() error {
return c.loadReactions(x)
func (c *Comment) LoadReactions(repo *Repository) error {
return c.loadReactions(x, repo)
}

func (c *Comment) loadReview(e Engine) (err error) {
Expand Down
38 changes: 24 additions & 14 deletions models/issue_reaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import (

// Reaction represents a reactions on issues and comments.
type Reaction struct {
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
User *User `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
OriginalAuthorID int64 `xorm:"INDEX UNIQUE(s) NOT NULL DEFAULT(0)"`
OriginalAuthor string
User *User `xorm:"-"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}

// FindReactionsOptions describes the conditions to Find reactions
Expand All @@ -49,7 +51,10 @@ func (opts *FindReactionsOptions) toConds() builder.Cond {
cond = cond.And(builder.Eq{"reaction.comment_id": 0})
}
if opts.UserID > 0 {
cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID})
cond = cond.And(builder.Eq{
"reaction.user_id": opts.UserID,
"reaction.original_author_id": 0,
})
}
if opts.Reaction != "" {
cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
Expand Down Expand Up @@ -173,7 +178,7 @@ func deleteReaction(e *xorm.Session, opts *ReactionOptions) error {
if opts.Comment != nil {
reaction.CommentID = opts.Comment.ID
}
_, err := e.Delete(reaction)
_, err := e.Where("original_author_id = 0").Delete(reaction)
return err
}

Expand Down Expand Up @@ -233,7 +238,7 @@ func (list ReactionList) HasUser(userID int64) bool {
return false
}
for _, reaction := range list {
if reaction.UserID == userID {
if reaction.OriginalAuthor == "" && reaction.UserID == userID {
return true
}
}
Expand All @@ -252,14 +257,17 @@ func (list ReactionList) GroupByType() map[string]ReactionList {
func (list ReactionList) getUserIDs() []int64 {
userIDs := make(map[int64]struct{}, len(list))
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
continue
}
if _, ok := userIDs[reaction.UserID]; !ok {
userIDs[reaction.UserID] = struct{}{}
}
}
return keysInt64(userIDs)
}

func (list ReactionList) loadUsers(e Engine) ([]*User, error) {
func (list ReactionList) loadUsers(e Engine, repo *Repository) ([]*User, error) {
if len(list) == 0 {
return nil, nil
}
Expand All @@ -274,7 +282,9 @@ func (list ReactionList) loadUsers(e Engine) ([]*User, error) {
}

for _, reaction := range list {
if user, ok := userMaps[reaction.UserID]; ok {
if reaction.OriginalAuthor != "" {
reaction.User = NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
} else if user, ok := userMaps[reaction.UserID]; ok {
reaction.User = user
} else {
reaction.User = NewGhostUser()
Expand All @@ -284,8 +294,8 @@ func (list ReactionList) loadUsers(e Engine) ([]*User, error) {
}

// LoadUsers loads reactions' all users
func (list ReactionList) LoadUsers() ([]*User, error) {
return list.loadUsers(x)
func (list ReactionList) LoadUsers(repo *Repository) ([]*User, error) {
return list.loadUsers(x, repo)
}

// GetFirstUsers returns first reacted user display names separated by comma
Expand Down
3 changes: 2 additions & 1 deletion models/issue_reaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)

issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
repo1 := AssertExistsAndLoadBean(t, &Repository{ID: issue1.RepoID}).(*Repository)

comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)

Expand All @@ -140,7 +141,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
addReaction(t, user3, issue1, comment1, "heart")
addReaction(t, user4, issue1, comment1, "+1")

err := comment1.LoadReactions()
err := comment1.LoadReactions(repo1)
assert.NoError(t, err)
assert.Len(t, comment1.Reactions, 4)

Expand Down
22 changes: 20 additions & 2 deletions models/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ func insertIssue(sess *xorm.Session, issue *Issue) error {
return err
}

for _, reaction := range issue.Reactions {
reaction.IssueID = issue.ID
}
if _, err := sess.Insert(issue.Reactions); err != nil {
return err
}

cols := make([]string, 0)
if !issue.IsPull {
sess.ID(issue.RepoID).Incr("num_issues")
Expand Down Expand Up @@ -130,9 +137,20 @@ func InsertIssueComments(comments []*Comment) error {
if err := sess.Begin(); err != nil {
return err
}
if _, err := sess.NoAutoTime().Insert(comments); err != nil {
return err
for _, comment := range comments {
if _, err := sess.NoAutoTime().Insert(comment); err != nil {
return err
}

for _, reaction := range comment.Reactions {
reaction.IssueID = comment.IssueID
reaction.CommentID = comment.ID
}
if _, err := sess.Insert(comment.Reactions); err != nil {
return err
}
}

for issueID := range issueIDs {
if _, err := sess.Exec("UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ?) WHERE id = ?", issueID, issueID); err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ var migrations = []Migration{
NewMigration("add is_restricted column for users table", addIsRestricted),
// v122 -> v123
NewMigration("Add Require Signed Commits to ProtectedBranch", addRequireSignedCommits),
// v123 -> v124
NewMigration("Add original informations for reactions", addReactionOriginals),
}

// Migrate database to current version
Expand Down
18 changes: 18 additions & 0 deletions models/migrations/v123.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2020 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 (
"xorm.io/xorm"
)

func addReactionOriginals(x *xorm.Engine) error {
type Reaction struct {
OriginalAuthorID int64 `xorm:"INDEX NOT NULL DEFAULT(0)"`
OriginalAuthor string
}

return x.Sync2(new(Reaction))
}
9 changes: 9 additions & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,15 @@ func NewGhostUser() *User {
}
}

// NewReplaceUser creates and returns a fake user for external user
func NewReplaceUser(name string) *User {
return &User{
ID: -1,
Name: name,
LowerName: strings.ToLower(name),
}
}

// IsGhost check if user is fake user for a deleted account
func (u *User) IsGhost() bool {
if u == nil {
Expand Down
2 changes: 1 addition & 1 deletion modules/migrations/base/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ type Comment struct {
Created time.Time
Updated time.Time
Content string
Reactions *Reactions
Reactions []*Reaction
}
2 changes: 1 addition & 1 deletion modules/migrations/base/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ type Issue struct {
Updated time.Time
Closed *time.Time
Labels []*Label
Reactions *Reactions
Reactions []*Reaction
}
1 change: 1 addition & 0 deletions modules/migrations/base/pullrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type PullRequest struct {
Assignee string
Assignees []string
IsLocked bool
Reactions []*Reaction
}

// IsForkPullRequest returns true if the pull request from a forked repository but not the same repository
Expand Down
17 changes: 6 additions & 11 deletions modules/migrations/base/reaction.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Copyright 2018 Jonas Franz. All rights reserved.
// Copyright 2020 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 base

// Reactions represents a summary of reactions.
type Reactions struct {
TotalCount int
PlusOne int
MinusOne int
Laugh int
Confused int
Heart int
Hooray int
// Reaction represents a reaction to an issue/pr/comment.
type Reaction struct {
UserID int64
UserName string
Content string
}
Loading

0 comments on commit 2b3e931

Please sign in to comment.