Migrate reactions when migrating repository from github (#9599)
* 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
This commit is contained in:
		
							parent
							
								
									4e566df1c6
								
							
						
					
					
						commit
						2b3e931cde
					
				
					 18 changed files with 329 additions and 101 deletions
				
			
		|  | @ -177,5 +177,9 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return UpdateReleasesMigrationsByType(tp, externalUserID, userID) | 	if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return UpdateReactionsMigrationsByType(tp, externalUserID, userID) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -218,8 +218,11 @@ func (issue *Issue) loadReactions(e Engine) (err error) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	if err = issue.loadRepo(e); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
| 	// Load reaction user data
 | 	// Load reaction user data
 | ||||||
| 	if _, err := ReactionList(reactions).loadUsers(e); err != nil { | 	if _, err := ReactionList(reactions).loadUsers(e, issue.Repo); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1836,3 +1839,17 @@ func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, origina | ||||||
| 		}) | 		}) | ||||||
| 	return err | 	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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -425,7 +425,7 @@ func (c *Comment) LoadDepIssueDetails() (err error) { | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Comment) loadReactions(e Engine) (err error) { | func (c *Comment) loadReactions(e Engine, repo *Repository) (err error) { | ||||||
| 	if c.Reactions != nil { | 	if c.Reactions != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  | @ -437,15 +437,15 @@ func (c *Comment) loadReactions(e Engine) (err error) { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	// Load reaction user data
 | 	// Load reaction user data
 | ||||||
| 	if _, err := c.Reactions.LoadUsers(); err != nil { | 	if _, err := c.Reactions.loadUsers(e, repo); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LoadReactions loads comment reactions
 | // LoadReactions loads comment reactions
 | ||||||
| func (c *Comment) LoadReactions() error { | func (c *Comment) LoadReactions(repo *Repository) error { | ||||||
| 	return c.loadReactions(x) | 	return c.loadReactions(x, repo) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *Comment) loadReview(e Engine) (err error) { | func (c *Comment) loadReview(e Engine) (err error) { | ||||||
|  |  | ||||||
|  | @ -17,13 +17,15 @@ import ( | ||||||
| 
 | 
 | ||||||
| // Reaction represents a reactions on issues and comments.
 | // Reaction represents a reactions on issues and comments.
 | ||||||
| type Reaction struct { | type Reaction struct { | ||||||
| 	ID          int64              `xorm:"pk autoincr"` | 	ID               int64  `xorm:"pk autoincr"` | ||||||
| 	Type        string             `xorm:"INDEX UNIQUE(s) NOT NULL"` | 	Type             string `xorm:"INDEX UNIQUE(s) NOT NULL"` | ||||||
| 	IssueID     int64              `xorm:"INDEX UNIQUE(s) NOT NULL"` | 	IssueID          int64  `xorm:"INDEX UNIQUE(s) NOT NULL"` | ||||||
| 	CommentID   int64              `xorm:"INDEX UNIQUE(s)"` | 	CommentID        int64  `xorm:"INDEX UNIQUE(s)"` | ||||||
| 	UserID      int64              `xorm:"INDEX UNIQUE(s) NOT NULL"` | 	UserID           int64  `xorm:"INDEX UNIQUE(s) NOT NULL"` | ||||||
| 	User        *User              `xorm:"-"` | 	OriginalAuthorID int64  `xorm:"INDEX UNIQUE(s) NOT NULL DEFAULT(0)"` | ||||||
| 	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | 	OriginalAuthor   string | ||||||
|  | 	User             *User              `xorm:"-"` | ||||||
|  | 	CreatedUnix      timeutil.TimeStamp `xorm:"INDEX created"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // FindReactionsOptions describes the conditions to Find reactions
 | // FindReactionsOptions describes the conditions to Find reactions
 | ||||||
|  | @ -49,7 +51,10 @@ func (opts *FindReactionsOptions) toConds() builder.Cond { | ||||||
| 		cond = cond.And(builder.Eq{"reaction.comment_id": 0}) | 		cond = cond.And(builder.Eq{"reaction.comment_id": 0}) | ||||||
| 	} | 	} | ||||||
| 	if opts.UserID > 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 != "" { | 	if opts.Reaction != "" { | ||||||
| 		cond = cond.And(builder.Eq{"reaction.type": opts.Reaction}) | 		cond = cond.And(builder.Eq{"reaction.type": opts.Reaction}) | ||||||
|  | @ -173,7 +178,7 @@ func deleteReaction(e *xorm.Session, opts *ReactionOptions) error { | ||||||
| 	if opts.Comment != nil { | 	if opts.Comment != nil { | ||||||
| 		reaction.CommentID = opts.Comment.ID | 		reaction.CommentID = opts.Comment.ID | ||||||
| 	} | 	} | ||||||
| 	_, err := e.Delete(reaction) | 	_, err := e.Where("original_author_id = 0").Delete(reaction) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -233,7 +238,7 @@ func (list ReactionList) HasUser(userID int64) bool { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	for _, reaction := range list { | 	for _, reaction := range list { | ||||||
| 		if reaction.UserID == userID { | 		if reaction.OriginalAuthor == "" && reaction.UserID == userID { | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -252,6 +257,9 @@ func (list ReactionList) GroupByType() map[string]ReactionList { | ||||||
| func (list ReactionList) getUserIDs() []int64 { | func (list ReactionList) getUserIDs() []int64 { | ||||||
| 	userIDs := make(map[int64]struct{}, len(list)) | 	userIDs := make(map[int64]struct{}, len(list)) | ||||||
| 	for _, reaction := range list { | 	for _, reaction := range list { | ||||||
|  | 		if reaction.OriginalAuthor != "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
| 		if _, ok := userIDs[reaction.UserID]; !ok { | 		if _, ok := userIDs[reaction.UserID]; !ok { | ||||||
| 			userIDs[reaction.UserID] = struct{}{} | 			userIDs[reaction.UserID] = struct{}{} | ||||||
| 		} | 		} | ||||||
|  | @ -259,7 +267,7 @@ func (list ReactionList) getUserIDs() []int64 { | ||||||
| 	return keysInt64(userIDs) | 	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 { | 	if len(list) == 0 { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
|  | @ -274,7 +282,9 @@ func (list ReactionList) loadUsers(e Engine) ([]*User, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, reaction := range list { | 	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 | 			reaction.User = user | ||||||
| 		} else { | 		} else { | ||||||
| 			reaction.User = NewGhostUser() | 			reaction.User = NewGhostUser() | ||||||
|  | @ -284,8 +294,8 @@ func (list ReactionList) loadUsers(e Engine) ([]*User, error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LoadUsers loads reactions' all users
 | // LoadUsers loads reactions' all users
 | ||||||
| func (list ReactionList) LoadUsers() ([]*User, error) { | func (list ReactionList) LoadUsers(repo *Repository) ([]*User, error) { | ||||||
| 	return list.loadUsers(x) | 	return list.loadUsers(x, repo) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetFirstUsers returns first reacted user display names separated by comma
 | // GetFirstUsers returns first reacted user display names separated by comma
 | ||||||
|  |  | ||||||
|  | @ -132,6 +132,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) { | ||||||
| 	user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) | 	user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) | ||||||
| 
 | 
 | ||||||
| 	issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | 	issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | ||||||
|  | 	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: issue1.RepoID}).(*Repository) | ||||||
| 
 | 
 | ||||||
| 	comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment) | 	comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment) | ||||||
| 
 | 
 | ||||||
|  | @ -140,7 +141,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) { | ||||||
| 	addReaction(t, user3, issue1, comment1, "heart") | 	addReaction(t, user3, issue1, comment1, "heart") | ||||||
| 	addReaction(t, user4, issue1, comment1, "+1") | 	addReaction(t, user4, issue1, comment1, "+1") | ||||||
| 
 | 
 | ||||||
| 	err := comment1.LoadReactions() | 	err := comment1.LoadReactions(repo1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, comment1.Reactions, 4) | 	assert.Len(t, comment1.Reactions, 4) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -63,6 +63,13 @@ func insertIssue(sess *xorm.Session, issue *Issue) error { | ||||||
| 		return err | 		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) | 	cols := make([]string, 0) | ||||||
| 	if !issue.IsPull { | 	if !issue.IsPull { | ||||||
| 		sess.ID(issue.RepoID).Incr("num_issues") | 		sess.ID(issue.RepoID).Incr("num_issues") | ||||||
|  | @ -130,9 +137,20 @@ func InsertIssueComments(comments []*Comment) error { | ||||||
| 	if err := sess.Begin(); err != nil { | 	if err := sess.Begin(); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if _, err := sess.NoAutoTime().Insert(comments); err != nil { | 	for _, comment := range comments { | ||||||
| 		return err | 		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 { | 	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 { | 		if _, err := sess.Exec("UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ?) WHERE id = ?", issueID, issueID); err != nil { | ||||||
| 			return err | 			return err | ||||||
|  |  | ||||||
|  | @ -300,6 +300,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("add is_restricted column for users table", addIsRestricted), | 	NewMigration("add is_restricted column for users table", addIsRestricted), | ||||||
| 	// v122 -> v123
 | 	// v122 -> v123
 | ||||||
| 	NewMigration("Add Require Signed Commits to ProtectedBranch", addRequireSignedCommits), | 	NewMigration("Add Require Signed Commits to ProtectedBranch", addRequireSignedCommits), | ||||||
|  | 	// v123 -> v124
 | ||||||
|  | 	NewMigration("Add original informations for reactions", addReactionOriginals), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Migrate database to current version
 | // Migrate database to current version
 | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								models/migrations/v123.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								models/migrations/v123.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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)) | ||||||
|  | } | ||||||
|  | @ -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
 | // IsGhost check if user is fake user for a deleted account
 | ||||||
| func (u *User) IsGhost() bool { | func (u *User) IsGhost() bool { | ||||||
| 	if u == nil { | 	if u == nil { | ||||||
|  |  | ||||||
|  | @ -16,5 +16,5 @@ type Comment struct { | ||||||
| 	Created     time.Time | 	Created     time.Time | ||||||
| 	Updated     time.Time | 	Updated     time.Time | ||||||
| 	Content     string | 	Content     string | ||||||
| 	Reactions   *Reactions | 	Reactions   []*Reaction | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,5 +22,5 @@ type Issue struct { | ||||||
| 	Updated     time.Time | 	Updated     time.Time | ||||||
| 	Closed      *time.Time | 	Closed      *time.Time | ||||||
| 	Labels      []*Label | 	Labels      []*Label | ||||||
| 	Reactions   *Reactions | 	Reactions   []*Reaction | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ type PullRequest struct { | ||||||
| 	Assignee       string | 	Assignee       string | ||||||
| 	Assignees      []string | 	Assignees      []string | ||||||
| 	IsLocked       bool | 	IsLocked       bool | ||||||
|  | 	Reactions      []*Reaction | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // IsForkPullRequest returns true if the pull request from a forked repository but not the same repository
 | // IsForkPullRequest returns true if the pull request from a forked repository but not the same repository
 | ||||||
|  |  | ||||||
|  | @ -1,17 +1,12 @@ | ||||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
| // Copyright 2018 Jonas Franz. All rights reserved.
 |  | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package base | package base | ||||||
| 
 | 
 | ||||||
| // Reactions represents a summary of reactions.
 | // Reaction represents a reaction to an issue/pr/comment.
 | ||||||
| type Reactions struct { | type Reaction struct { | ||||||
| 	TotalCount int | 	UserID   int64 | ||||||
| 	PlusOne    int | 	UserName string | ||||||
| 	MinusOne   int | 	Content  string | ||||||
| 	Laugh      int |  | ||||||
| 	Confused   int |  | ||||||
| 	Heart      int |  | ||||||
| 	Hooray     int |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -361,7 +361,32 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error { | ||||||
| 		if issue.Closed != nil { | 		if issue.Closed != nil { | ||||||
| 			is.ClosedUnix = timeutil.TimeStamp(issue.Closed.Unix()) | 			is.ClosedUnix = timeutil.TimeStamp(issue.Closed.Unix()) | ||||||
| 		} | 		} | ||||||
| 		// TODO: add reactions
 | 		// add reactions
 | ||||||
|  | 		for _, reaction := range issue.Reactions { | ||||||
|  | 			userid, ok := g.userMap[reaction.UserID] | ||||||
|  | 			if !ok && tp != "" { | ||||||
|  | 				var err error | ||||||
|  | 				userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
|  | 				} | ||||||
|  | 				if userid > 0 { | ||||||
|  | 					g.userMap[reaction.UserID] = userid | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			var res = models.Reaction{ | ||||||
|  | 				Type:        reaction.Content, | ||||||
|  | 				CreatedUnix: timeutil.TimeStampNow(), | ||||||
|  | 			} | ||||||
|  | 			if userid > 0 { | ||||||
|  | 				res.UserID = userid | ||||||
|  | 			} else { | ||||||
|  | 				res.UserID = g.doer.ID | ||||||
|  | 				res.OriginalAuthorID = reaction.UserID | ||||||
|  | 				res.OriginalAuthor = reaction.UserName | ||||||
|  | 			} | ||||||
|  | 			is.Reactions = append(is.Reactions, &res) | ||||||
|  | 		} | ||||||
| 		iss = append(iss, &is) | 		iss = append(iss, &is) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -420,9 +445,34 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { | ||||||
| 			cm.OriginalAuthorID = comment.PosterID | 			cm.OriginalAuthorID = comment.PosterID | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		cms = append(cms, &cm) | 		// add reactions
 | ||||||
|  | 		for _, reaction := range comment.Reactions { | ||||||
|  | 			userid, ok := g.userMap[reaction.UserID] | ||||||
|  | 			if !ok && tp != "" { | ||||||
|  | 				var err error | ||||||
|  | 				userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
|  | 				if err != nil { | ||||||
|  | 					log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
|  | 				} | ||||||
|  | 				if userid > 0 { | ||||||
|  | 					g.userMap[reaction.UserID] = userid | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			var res = models.Reaction{ | ||||||
|  | 				Type:        reaction.Content, | ||||||
|  | 				CreatedUnix: timeutil.TimeStampNow(), | ||||||
|  | 			} | ||||||
|  | 			if userid > 0 { | ||||||
|  | 				res.UserID = userid | ||||||
|  | 			} else { | ||||||
|  | 				res.UserID = g.doer.ID | ||||||
|  | 				res.OriginalAuthorID = reaction.UserID | ||||||
|  | 				res.OriginalAuthor = reaction.UserName | ||||||
|  | 			} | ||||||
|  | 			cm.Reactions = append(cm.Reactions, &res) | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// TODO: Reactions
 | 		cms = append(cms, &cm) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return models.InsertIssueComments(cms) | 	return models.InsertIssueComments(cms) | ||||||
|  | @ -581,10 +631,12 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | ||||||
| 		UpdatedUnix: timeutil.TimeStamp(pr.Updated.Unix()), | 		UpdatedUnix: timeutil.TimeStamp(pr.Updated.Unix()), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	tp := g.gitServiceType.Name() | ||||||
|  | 
 | ||||||
| 	userid, ok := g.userMap[pr.PosterID] | 	userid, ok := g.userMap[pr.PosterID] | ||||||
| 	if !ok { | 	if !ok && tp != "" { | ||||||
| 		var err error | 		var err error | ||||||
| 		userid, err = models.GetUserIDByExternalUserID("github", fmt.Sprintf("%v", pr.PosterID)) | 		userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetUserIDByExternalUserID: %v", err) | 			log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 		} | 		} | ||||||
|  | @ -601,6 +653,33 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | ||||||
| 		issue.OriginalAuthorID = pr.PosterID | 		issue.OriginalAuthorID = pr.PosterID | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// add reactions
 | ||||||
|  | 	for _, reaction := range pr.Reactions { | ||||||
|  | 		userid, ok := g.userMap[reaction.UserID] | ||||||
|  | 		if !ok && tp != "" { | ||||||
|  | 			var err error | ||||||
|  | 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
|  | 			} | ||||||
|  | 			if userid > 0 { | ||||||
|  | 				g.userMap[reaction.UserID] = userid | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		var res = models.Reaction{ | ||||||
|  | 			Type:        reaction.Content, | ||||||
|  | 			CreatedUnix: timeutil.TimeStampNow(), | ||||||
|  | 		} | ||||||
|  | 		if userid > 0 { | ||||||
|  | 			res.UserID = userid | ||||||
|  | 		} else { | ||||||
|  | 			res.UserID = g.doer.ID | ||||||
|  | 			res.OriginalAuthorID = reaction.UserID | ||||||
|  | 			res.OriginalAuthor = reaction.UserName | ||||||
|  | 		} | ||||||
|  | 		issue.Reactions = append(issue.Reactions, &res) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var pullRequest = models.PullRequest{ | 	var pullRequest = models.PullRequest{ | ||||||
| 		HeadRepoID: g.repo.ID, | 		HeadRepoID: g.repo.ID, | ||||||
| 		HeadBranch: head, | 		HeadBranch: head, | ||||||
|  | @ -622,7 +701,6 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | ||||||
| 		pullRequest.MergerID = g.doer.ID | 		pullRequest.MergerID = g.doer.ID | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// TODO: reactions
 |  | ||||||
| 	// TODO: assignees
 | 	// TODO: assignees
 | ||||||
| 
 | 
 | ||||||
| 	return &pullRequest, nil | 	return &pullRequest, nil | ||||||
|  |  | ||||||
|  | @ -319,18 +319,6 @@ func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) { | ||||||
| 	return releases, nil | 	return releases, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func convertGithubReactions(reactions *github.Reactions) *base.Reactions { |  | ||||||
| 	return &base.Reactions{ |  | ||||||
| 		TotalCount: *reactions.TotalCount, |  | ||||||
| 		PlusOne:    *reactions.PlusOne, |  | ||||||
| 		MinusOne:   *reactions.MinusOne, |  | ||||||
| 		Laugh:      *reactions.Laugh, |  | ||||||
| 		Confused:   *reactions.Confused, |  | ||||||
| 		Heart:      *reactions.Heart, |  | ||||||
| 		Hooray:     *reactions.Hooray, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // GetIssues returns issues according start and limit
 | // GetIssues returns issues according start and limit
 | ||||||
| func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { | func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { | ||||||
| 	opt := &github.IssueListByRepoOptions{ | 	opt := &github.IssueListByRepoOptions{ | ||||||
|  | @ -366,15 +354,36 @@ func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool, | ||||||
| 		for _, l := range issue.Labels { | 		for _, l := range issue.Labels { | ||||||
| 			labels = append(labels, convertGithubLabel(&l)) | 			labels = append(labels, convertGithubLabel(&l)) | ||||||
| 		} | 		} | ||||||
| 		var reactions *base.Reactions |  | ||||||
| 		if issue.Reactions != nil { |  | ||||||
| 			reactions = convertGithubReactions(issue.Reactions) |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		var email string | 		var email string | ||||||
| 		if issue.User.Email != nil { | 		if issue.User.Email != nil { | ||||||
| 			email = *issue.User.Email | 			email = *issue.User.Email | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		// get reactions
 | ||||||
|  | 		var reactions []*base.Reaction | ||||||
|  | 		for i := 1; ; i++ { | ||||||
|  | 			g.sleep() | ||||||
|  | 			res, resp, err := g.client.Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, issue.GetNumber(), &github.ListOptions{ | ||||||
|  | 				Page:    i, | ||||||
|  | 				PerPage: perPage, | ||||||
|  | 			}) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, false, err | ||||||
|  | 			} | ||||||
|  | 			g.rate = &resp.Rate | ||||||
|  | 			if len(res) == 0 { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			for _, reaction := range res { | ||||||
|  | 				reactions = append(reactions, &base.Reaction{ | ||||||
|  | 					UserID:   reaction.User.GetID(), | ||||||
|  | 					UserName: reaction.User.GetLogin(), | ||||||
|  | 					Content:  reaction.GetContent(), | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		allIssues = append(allIssues, &base.Issue{ | 		allIssues = append(allIssues, &base.Issue{ | ||||||
| 			Title:       *issue.Title, | 			Title:       *issue.Title, | ||||||
| 			Number:      int64(*issue.Number), | 			Number:      int64(*issue.Number), | ||||||
|  | @ -418,9 +427,29 @@ func (g *GithubDownloaderV3) GetComments(issueNumber int64) ([]*base.Comment, er | ||||||
| 			if comment.User.Email != nil { | 			if comment.User.Email != nil { | ||||||
| 				email = *comment.User.Email | 				email = *comment.User.Email | ||||||
| 			} | 			} | ||||||
| 			var reactions *base.Reactions | 
 | ||||||
| 			if comment.Reactions != nil { | 			// get reactions
 | ||||||
| 				reactions = convertGithubReactions(comment.Reactions) | 			var reactions []*base.Reaction | ||||||
|  | 			for i := 1; ; i++ { | ||||||
|  | 				g.sleep() | ||||||
|  | 				res, resp, err := g.client.Reactions.ListIssueCommentReactions(g.ctx, g.repoOwner, g.repoName, comment.GetID(), &github.ListOptions{ | ||||||
|  | 					Page:    i, | ||||||
|  | 					PerPage: 100, | ||||||
|  | 				}) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				g.rate = &resp.Rate | ||||||
|  | 				if len(res) == 0 { | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 				for _, reaction := range res { | ||||||
|  | 					reactions = append(reactions, &base.Reaction{ | ||||||
|  | 						UserID:   reaction.User.GetID(), | ||||||
|  | 						UserName: reaction.User.GetLogin(), | ||||||
|  | 						Content:  reaction.GetContent(), | ||||||
|  | 					}) | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			allComments = append(allComments, &base.Comment{ | 			allComments = append(allComments, &base.Comment{ | ||||||
| 				IssueIndex:  issueNumber, | 				IssueIndex:  issueNumber, | ||||||
|  | @ -473,8 +502,6 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq | ||||||
| 			labels = append(labels, convertGithubLabel(l)) | 			labels = append(labels, convertGithubLabel(l)) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// FIXME: This API missing reactions, we may need another extra request to get reactions
 |  | ||||||
| 
 |  | ||||||
| 		var email string | 		var email string | ||||||
| 		if pr.User.Email != nil { | 		if pr.User.Email != nil { | ||||||
| 			email = *pr.User.Email | 			email = *pr.User.Email | ||||||
|  | @ -515,6 +542,30 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq | ||||||
| 			headUserName = *pr.Head.User.Login | 			headUserName = *pr.Head.User.Login | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		// get reactions
 | ||||||
|  | 		var reactions []*base.Reaction | ||||||
|  | 		for i := 1; ; i++ { | ||||||
|  | 			g.sleep() | ||||||
|  | 			res, resp, err := g.client.Reactions.ListIssueReactions(g.ctx, g.repoOwner, g.repoName, pr.GetNumber(), &github.ListOptions{ | ||||||
|  | 				Page:    i, | ||||||
|  | 				PerPage: perPage, | ||||||
|  | 			}) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			g.rate = &resp.Rate | ||||||
|  | 			if len(res) == 0 { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			for _, reaction := range res { | ||||||
|  | 				reactions = append(reactions, &base.Reaction{ | ||||||
|  | 					UserID:   reaction.User.GetID(), | ||||||
|  | 					UserName: reaction.User.GetLogin(), | ||||||
|  | 					Content:  reaction.GetContent(), | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		allPRs = append(allPRs, &base.PullRequest{ | 		allPRs = append(allPRs, &base.PullRequest{ | ||||||
| 			Title:          *pr.Title, | 			Title:          *pr.Title, | ||||||
| 			Number:         int64(*pr.Number), | 			Number:         int64(*pr.Number), | ||||||
|  | @ -545,7 +596,8 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq | ||||||
| 				RepoName:  *pr.Base.Repo.Name, | 				RepoName:  *pr.Base.Repo.Name, | ||||||
| 				OwnerName: *pr.Base.User.Login, | 				OwnerName: *pr.Base.User.Login, | ||||||
| 			}, | 			}, | ||||||
| 			PatchURL: *pr.PatchURL, | 			PatchURL:  *pr.PatchURL, | ||||||
|  | 			Reactions: reactions, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -170,14 +170,12 @@ func TestGitHubDownloadRepo(t *testing.T) { | ||||||
| 					Description: "Good for newcomers", | 					Description: "Good for newcomers", | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Reactions: &base.Reactions{ | 			Reactions: []*base.Reaction{ | ||||||
| 				TotalCount: 1, | 				{ | ||||||
| 				PlusOne:    1, | 					UserID:   1669571, | ||||||
| 				MinusOne:   0, | 					UserName: "mrsdizzie", | ||||||
| 				Laugh:      0, | 					Content:  "+1", | ||||||
| 				Confused:   0, | 				}, | ||||||
| 				Heart:      0, |  | ||||||
| 				Hooray:     0, |  | ||||||
| 			}, | 			}, | ||||||
| 			Closed: &closed1, | 			Closed: &closed1, | ||||||
| 		}, | 		}, | ||||||
|  | @ -198,14 +196,37 @@ func TestGitHubDownloadRepo(t *testing.T) { | ||||||
| 					Description: "This issue or pull request already exists", | 					Description: "This issue or pull request already exists", | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Reactions: &base.Reactions{ | 			Reactions: []*base.Reaction{ | ||||||
| 				TotalCount: 6, | 				{ | ||||||
| 				PlusOne:    1, | 					UserID:   1669571, | ||||||
| 				MinusOne:   1, | 					UserName: "mrsdizzie", | ||||||
| 				Laugh:      1, | 					Content:  "heart", | ||||||
| 				Confused:   1, | 				}, | ||||||
| 				Heart:      1, | 				{ | ||||||
| 				Hooray:     1, | 					UserID:   1669571, | ||||||
|  | 					UserName: "mrsdizzie", | ||||||
|  | 					Content:  "laugh", | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					UserID:   1669571, | ||||||
|  | 					UserName: "mrsdizzie", | ||||||
|  | 					Content:  "-1", | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					UserID:   1669571, | ||||||
|  | 					UserName: "mrsdizzie", | ||||||
|  | 					Content:  "confused", | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					UserID:   1669571, | ||||||
|  | 					UserName: "mrsdizzie", | ||||||
|  | 					Content:  "hooray", | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					UserID:   1669571, | ||||||
|  | 					UserName: "mrsdizzie", | ||||||
|  | 					Content:  "+1", | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			Closed: &closed2, | 			Closed: &closed2, | ||||||
| 		}, | 		}, | ||||||
|  | @ -223,14 +244,12 @@ func TestGitHubDownloadRepo(t *testing.T) { | ||||||
| 			Created:    time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), | 			Created:    time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), | ||||||
| 			Updated:    time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), | 			Updated:    time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), | ||||||
| 			Content:    "This is a comment", | 			Content:    "This is a comment", | ||||||
| 			Reactions: &base.Reactions{ | 			Reactions: []*base.Reaction{ | ||||||
| 				TotalCount: 1, | 				{ | ||||||
| 				PlusOne:    1, | 					UserID:   1669571, | ||||||
| 				MinusOne:   0, | 					UserName: "mrsdizzie", | ||||||
| 				Laugh:      0, | 					Content:  "+1", | ||||||
| 				Confused:   0, | 				}, | ||||||
| 				Heart:      0, |  | ||||||
| 				Hooray:     0, |  | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|  | @ -240,15 +259,7 @@ func TestGitHubDownloadRepo(t *testing.T) { | ||||||
| 			Created:    time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), | 			Created:    time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), | ||||||
| 			Updated:    time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), | 			Updated:    time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), | ||||||
| 			Content:    "A second comment", | 			Content:    "A second comment", | ||||||
| 			Reactions: &base.Reactions{ | 			Reactions:  nil, | ||||||
| 				TotalCount: 0, |  | ||||||
| 				PlusOne:    0, |  | ||||||
| 				MinusOne:   0, |  | ||||||
| 				Laugh:      0, |  | ||||||
| 				Confused:   0, |  | ||||||
| 				Heart:      0, |  | ||||||
| 				Hooray:     0, |  | ||||||
| 			}, |  | ||||||
| 		}, | 		}, | ||||||
| 	}, comments[:2]) | 	}, comments[:2]) | ||||||
| 
 | 
 | ||||||
|  | @ -331,6 +342,18 @@ func TestGitHubDownloadRepo(t *testing.T) { | ||||||
| 			}, | 			}, | ||||||
| 			Merged:         false, | 			Merged:         false, | ||||||
| 			MergeCommitSHA: "565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae", | 			MergeCommitSHA: "565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae", | ||||||
|  | 			Reactions: []*base.Reaction{ | ||||||
|  | 				{ | ||||||
|  | 					UserID:   81045, | ||||||
|  | 					UserName: "lunny", | ||||||
|  | 					Content:  "heart", | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					UserID:   81045, | ||||||
|  | 					UserName: "lunny", | ||||||
|  | 					Content:  "+1", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
| 		}, | 		}, | ||||||
| 	}, prs) | 	}, prs) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -65,7 +65,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) | 		ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	_, err = reactions.LoadUsers() | 	_, err = reactions.LoadUsers(ctx.Repo.Repository) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err) | 		ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err) | ||||||
| 		return | 		return | ||||||
|  | @ -271,7 +271,7 @@ func GetIssueReactions(ctx *context.APIContext) { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) | 		ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	_, err = reactions.LoadUsers() | 	_, err = reactions.LoadUsers(ctx.Repo.Repository) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err) | 		ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err) | ||||||
| 		return | 		return | ||||||
|  |  | ||||||
|  | @ -1608,7 +1608,7 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { | ||||||
| 		} | 		} | ||||||
| 		// Reload new reactions
 | 		// Reload new reactions
 | ||||||
| 		comment.Reactions = nil | 		comment.Reactions = nil | ||||||
| 		if err = comment.LoadReactions(); err != nil { | 		if err = comment.LoadReactions(ctx.Repo.Repository); err != nil { | ||||||
| 			log.Info("comment.LoadReactions: %s", err) | 			log.Info("comment.LoadReactions: %s", err) | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  | @ -1622,7 +1622,7 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { | ||||||
| 
 | 
 | ||||||
| 		// Reload new reactions
 | 		// Reload new reactions
 | ||||||
| 		comment.Reactions = nil | 		comment.Reactions = nil | ||||||
| 		if err = comment.LoadReactions(); err != nil { | 		if err = comment.LoadReactions(ctx.Repo.Repository); err != nil { | ||||||
| 			log.Info("comment.LoadReactions: %s", err) | 			log.Info("comment.LoadReactions: %s", err) | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue