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:
zeripath 2020-10-24 15:11:30 +01:00 committed by GitHub
parent 2fa4c4ad3a
commit 4099e4f1b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 3 deletions

View file

@ -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
View 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
}

View file

@ -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)