Browse Source

Refactor topic Find functions and add more tests for pagination (#30127)

This also fixed #22238
tags/v1.22.0-rc1
Lunny Xiao 1 month ago
parent
commit
8acc7aab4c
No account linked to committer's email address

+ 13
- 18
models/repo/topic.go View File

Keyword string Keyword string
} }


func (opts *FindTopicOptions) toConds() builder.Cond {
func (opts *FindTopicOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_topic.repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_topic.repo_id": opts.RepoID})
return cond return cond
} }


// FindTopics retrieves the topics via FindTopicOptions
func FindTopics(ctx context.Context, opts *FindTopicOptions) ([]*Topic, int64, error) {
sess := db.GetEngine(ctx).Select("topic.*").Where(opts.toConds())
func (opts *FindTopicOptions) ToOrders() string {
orderBy := "topic.repo_count DESC" orderBy := "topic.repo_count DESC"
if opts.RepoID > 0 { if opts.RepoID > 0 {
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result
} }
if opts.PageSize != 0 && opts.Page != 0 {
sess = db.SetSessionPagination(sess, opts)
}
topics := make([]*Topic, 0, 10)
total, err := sess.OrderBy(orderBy).FindAndCount(&topics)
return topics, total, err
return orderBy
} }


// CountTopics counts the number of topics matching the FindTopicOptions
func CountTopics(ctx context.Context, opts *FindTopicOptions) (int64, error) {
sess := db.GetEngine(ctx).Where(opts.toConds())
if opts.RepoID > 0 {
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
func (opts *FindTopicOptions) ToJoins() []db.JoinFunc {
if opts.RepoID <= 0 {
return nil
}
return []db.JoinFunc{
func(e db.Engine) error {
e.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
return nil
},
} }
return sess.Count(new(Topic))
} }


// GetRepoTopicByName retrieves topic from name for a repo if it exist // GetRepoTopicByName retrieves topic from name for a repo if it exist


// SaveTopics save topics to a repository // SaveTopics save topics to a repository
func SaveTopics(ctx context.Context, repoID int64, topicNames ...string) error { func SaveTopics(ctx context.Context, repoID int64, topicNames ...string) error {
topics, _, err := FindTopics(ctx, &FindTopicOptions{
topics, err := db.Find[Topic](ctx, &FindTopicOptions{
RepoID: repoID, RepoID: repoID,
}) })
if err != nil { if err != nil {

+ 7
- 7
models/repo/topic_test.go View File



assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())


topics, _, err := repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{})
topics, err := db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)


topics, total, err := repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{
topics, total, err := db.FindAndCount[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 2}, ListOptions: db.ListOptions{Page: 1, PageSize: 2},
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, 2) assert.Len(t, topics, 2)
assert.EqualValues(t, 6, total) assert.EqualValues(t, 6, total)


topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{
RepoID: 1, RepoID: 1,
}) })
assert.NoError(t, err) assert.NoError(t, err)


assert.NoError(t, repo_model.SaveTopics(db.DefaultContext, 2, "golang")) assert.NoError(t, repo_model.SaveTopics(db.DefaultContext, 2, "golang"))
repo2NrOfTopics := 1 repo2NrOfTopics := 1
topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{})
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)


topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{
RepoID: 2, RepoID: 2,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, topic.RepoCount) assert.EqualValues(t, 1, topic.RepoCount)


topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{})
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, topics, totalNrOfTopics) assert.Len(t, topics, totalNrOfTopics)


topics, _, err = repo_model.FindTopics(db.DefaultContext, &repo_model.FindTopicOptions{
topics, err = db.Find[repo_model.Topic](db.DefaultContext, &repo_model.FindTopicOptions{
RepoID: 2, RepoID: 2,
}) })
assert.NoError(t, err) assert.NoError(t, err)

+ 4
- 3
routers/api/v1/repo/topic.go View File

"net/http" "net/http"
"strings" "strings"


"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
} }


topics, total, err := repo_model.FindTopics(ctx, opts)
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }


// Prevent adding more topics than allowed to repo // Prevent adding more topics than allowed to repo
count, err := repo_model.CountTopics(ctx, &repo_model.FindTopicOptions{
count, err := db.Count[repo_model.Topic](ctx, &repo_model.FindTopicOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) })
if err != nil { if err != nil {
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
} }


topics, total, err := repo_model.FindTopics(ctx, opts)
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

+ 1
- 1
routers/web/explore/topic.go View File

}, },
} }


topics, total, err := repo_model.FindTopics(ctx, opts)
topics, total, err := db.FindAndCount[repo_model.Topic](ctx, opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError) ctx.Error(http.StatusInternalServerError)
return return

+ 1
- 1
routers/web/repo/view.go View File

} }


func renderRepoTopics(ctx *context.Context) { func renderRepoTopics(ctx *context.Context) {
topics, _, err := repo_model.FindTopics(ctx, &repo_model.FindTopicOptions{
topics, err := db.Find[repo_model.Topic](ctx, &repo_model.FindTopicOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) })
if err != nil { if err != nil {

+ 21
- 1
tests/integration/api_repo_topic_test.go View File

TopicNames []*api.TopicResponse `json:"topics"` TopicNames []*api.TopicResponse `json:"topics"`
} }


// search all topics
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 6)
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))

// pagination search topics first page
topics.TopicNames = nil
query := url.Values{"page": []string{"1"}, "limit": []string{"4"}} query := url.Values{"page": []string{"1"}, "limit": []string{"4"}}


searchURL.RawQuery = query.Encode() searchURL.RawQuery = query.Encode()
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics) DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 4) assert.Len(t, topics.TopicNames, 4)
assert.EqualValues(t, "6", res.Header().Get("x-total-count")) assert.EqualValues(t, "6", res.Header().Get("x-total-count"))


// pagination search topics second page
topics.TopicNames = nil
query = url.Values{"page": []string{"2"}, "limit": []string{"4"}}

searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2)
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))

// add keyword search
query = url.Values{"page": []string{"1"}, "limit": []string{"4"}}
query.Add("q", "topic") query.Add("q", "topic")
searchURL.RawQuery = query.Encode() searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)

+ 23
- 1
tests/integration/repo_topic_test.go View File

TopicNames []*api.TopicResponse `json:"topics"` TopicNames []*api.TopicResponse `json:"topics"`
} }


// search all topics
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 6)
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))

// pagination search topics
topics.TopicNames = nil
query := url.Values{"page": []string{"1"}, "limit": []string{"4"}} query := url.Values{"page": []string{"1"}, "limit": []string{"4"}}


searchURL.RawQuery = query.Encode() searchURL.RawQuery = query.Encode()
res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics) DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 4) assert.Len(t, topics.TopicNames, 4)
assert.EqualValues(t, "6", res.Header().Get("x-total-count")) assert.EqualValues(t, "6", res.Header().Get("x-total-count"))


// second page
topics.TopicNames = nil
query = url.Values{"page": []string{"2"}, "limit": []string{"4"}}

searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2)
assert.EqualValues(t, "6", res.Header().Get("x-total-count"))

// add keyword search
topics.TopicNames = nil
query = url.Values{"page": []string{"1"}, "limit": []string{"4"}}
query.Add("q", "topic") query.Add("q", "topic")
searchURL.RawQuery = query.Encode() searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)
DecodeJSON(t, res, &topics) DecodeJSON(t, res, &topics)
assert.Len(t, topics.TopicNames, 2) assert.Len(t, topics.TopicNames, 2)


topics.TopicNames = nil
query.Set("q", "database") query.Set("q", "database")
searchURL.RawQuery = query.Encode() searchURL.RawQuery = query.Encode()
res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK)

Loading…
Cancel
Save