Ensure topics added using the API are added to the repository (#13285)
* Ensure topics added using the API are added to the repository Fix #12426 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		
							parent
							
								
									2fa4c4ad3a
								
							
						
					
					
						commit
						4099e4f1b6
					
				
					 3 changed files with 100 additions and 3 deletions
				
			
		|  | @ -248,6 +248,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("add changed_protected_files column for pull_request table", addChangedProtectedFilesPullRequestColumn), | 	NewMigration("add changed_protected_files column for pull_request table", addChangedProtectedFilesPullRequestColumn), | ||||||
| 	// v156 -> v157
 | 	// v156 -> v157
 | ||||||
| 	NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases), | 	NewMigration("fix publisher ID for tag releases", fixPublisherIDforTagReleases), | ||||||
|  | 	// v157 -> v158
 | ||||||
|  | 	NewMigration("ensure repo topics are up-to-date", fixRepoTopics), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetCurrentDBVersion returns the current db version
 | // GetCurrentDBVersion returns the current db version
 | ||||||
|  |  | ||||||
							
								
								
									
										68
									
								
								models/migrations/v157.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								models/migrations/v157.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | // 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 fixRepoTopics(x *xorm.Engine) error { | ||||||
|  | 
 | ||||||
|  | 	type Topic struct { | ||||||
|  | 		ID        int64  `xorm:"pk autoincr"` | ||||||
|  | 		Name      string `xorm:"UNIQUE VARCHAR(25)"` | ||||||
|  | 		RepoCount int | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	type RepoTopic struct { | ||||||
|  | 		RepoID  int64 `xorm:"pk"` | ||||||
|  | 		TopicID int64 `xorm:"pk"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	type Repository struct { | ||||||
|  | 		ID     int64    `xorm:"pk autoincr"` | ||||||
|  | 		Topics []string `xorm:"TEXT JSON"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const batchSize = 100 | ||||||
|  | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	repos := make([]*Repository, 0, batchSize) | ||||||
|  | 	topics := make([]string, 0, batchSize) | ||||||
|  | 	for start := 0; ; start += batchSize { | ||||||
|  | 		repos = repos[:0] | ||||||
|  | 
 | ||||||
|  | 		if err := sess.Begin(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := sess.Limit(batchSize, start).Find(&repos); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if len(repos) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, repo := range repos { | ||||||
|  | 			topics = topics[:0] | ||||||
|  | 			if err := sess.Select("name").Table("topic"). | ||||||
|  | 				Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). | ||||||
|  | 				Where("repo_topic.repo_id = ?", repo.ID).Desc("topic.repo_count").Find(&topics); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 			repo.Topics = topics | ||||||
|  | 			if _, err := sess.ID(repo.ID).Cols("topics").Update(repo); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err := sess.Commit(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -197,10 +197,13 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) { | ||||||
| 
 | 
 | ||||||
| // GetRepoTopicByName retrives topic from name for a repo if it exist
 | // GetRepoTopicByName retrives topic from name for a repo if it exist
 | ||||||
| func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { | func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { | ||||||
|  | 	return getRepoTopicByName(x, repoID, topicName) | ||||||
|  | } | ||||||
|  | func getRepoTopicByName(e Engine, repoID int64, topicName string) (*Topic, error) { | ||||||
| 	var cond = builder.NewCond() | 	var cond = builder.NewCond() | ||||||
| 	var topic Topic | 	var topic Topic | ||||||
| 	cond = cond.And(builder.Eq{"repo_topic.repo_id": repoID}).And(builder.Eq{"topic.name": topicName}) | 	cond = cond.And(builder.Eq{"repo_topic.repo_id": repoID}).And(builder.Eq{"topic.name": topicName}) | ||||||
| 	sess := x.Table("topic").Where(cond) | 	sess := e.Table("topic").Where(cond) | ||||||
| 	sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") | 	sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") | ||||||
| 	has, err := sess.Get(&topic) | 	has, err := sess.Get(&topic) | ||||||
| 	if has { | 	if has { | ||||||
|  | @ -211,7 +214,13 @@ func GetRepoTopicByName(repoID int64, topicName string) (*Topic, error) { | ||||||
| 
 | 
 | ||||||
| // AddTopic adds a topic name to a repository (if it does not already have it)
 | // AddTopic adds a topic name to a repository (if it does not already have it)
 | ||||||
| func AddTopic(repoID int64, topicName string) (*Topic, error) { | func AddTopic(repoID int64, topicName string) (*Topic, error) { | ||||||
| 	topic, err := GetRepoTopicByName(repoID, topicName) | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	topic, err := getRepoTopicByName(sess, repoID, topicName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -220,7 +229,25 @@ func AddTopic(repoID int64, topicName string) (*Topic, error) { | ||||||
| 		return topic, nil | 		return topic, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return addTopicByNameToRepo(x, repoID, topicName) | 	topic, err = addTopicByNameToRepo(sess, repoID, topicName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	topicNames := make([]string, 0, 25) | ||||||
|  | 	if err := sess.Select("name").Table("topic"). | ||||||
|  | 		Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id"). | ||||||
|  | 		Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{ | ||||||
|  | 		Topics: topicNames, | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return topic, sess.Commit() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteTopic removes a topic name from a repository (if it has it)
 | // DeleteTopic removes a topic name from a repository (if it has it)
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue