summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBo-Yi Wu <appleboy.tw@gmail.com>2018-09-13 10:33:48 +0800
committerGitHub <noreply@github.com>2018-09-13 10:33:48 +0800
commitea20adaa84cbff762fcaa7f2d5fa6b7bf56706ec (patch)
treec2d09da1dcb0a00654963b30dbf5eeb152c83857
parent7dd93b2441fc4eac89ccf6b919bf46823334c8bf (diff)
downloadgitea-ea20adaa84cbff762fcaa7f2d5fa6b7bf56706ec.tar.gz
gitea-ea20adaa84cbff762fcaa7f2d5fa6b7bf56706ec.zip
feat(repo): support search repository by topic name (#4505)
* feat(repo): support search repository by topic name
-rw-r--r--integrations/api_admin_test.go4
-rw-r--r--integrations/api_issue_test.go2
-rw-r--r--integrations/api_repo_test.go6
-rw-r--r--models/fixtures/repo_topic.yml8
-rw-r--r--models/fixtures/repository.yml22
-rw-r--r--models/fixtures/topic.yml6
-rw-r--r--models/fixtures/user.yml15
-rw-r--r--models/repo_list.go33
-rw-r--r--models/repo_list_test.go31
-rw-r--r--models/ssh_key.go8
-rw-r--r--models/topic_test.go6
-rw-r--r--models/user_test.go4
-rw-r--r--routers/api/v1/repo/repo.go1
-rw-r--r--routers/home.go2
-rw-r--r--routers/user/profile.go4
-rw-r--r--templates/explore/repo_list.tmpl2
-rw-r--r--templates/repo/home.tmpl6
17 files changed, 134 insertions, 26 deletions
diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go
index f801b08d39..690edad757 100644
--- a/integrations/api_admin_test.go
+++ b/integrations/api_admin_test.go
@@ -9,10 +9,10 @@ import (
"net/http"
"testing"
- "github.com/stretchr/testify/assert"
-
"code.gitea.io/gitea/models"
api "code.gitea.io/sdk/gitea"
+
+ "github.com/stretchr/testify/assert"
)
func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go
index 97207f3368..0ac2f3b67e 100644
--- a/integrations/api_issue_test.go
+++ b/integrations/api_issue_test.go
@@ -5,13 +5,13 @@
package integrations
import (
+ "fmt"
"net/http"
"testing"
"code.gitea.io/gitea/models"
api "code.gitea.io/sdk/gitea"
- "fmt"
"github.com/stretchr/testify/assert"
)
diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go
index 3fa2ae21dc..62237e2be3 100644
--- a/integrations/api_repo_test.go
+++ b/integrations/api_repo_test.go
@@ -67,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) {
expectedResults
}{
{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{
- nil: {count: 17},
- user: {count: 17},
- user2: {count: 17}},
+ nil: {count: 19},
+ user: {count: 19},
+ user2: {count: 19}},
},
{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{
nil: {count: 10},
diff --git a/models/fixtures/repo_topic.yml b/models/fixtures/repo_topic.yml
index 58937031cd..7041ccfd09 100644
--- a/models/fixtures/repo_topic.yml
+++ b/models/fixtures/repo_topic.yml
@@ -9,3 +9,11 @@
-
repo_id: 1
topic_id: 3
+
+-
+ repo_id: 33
+ topic_id: 1
+
+-
+ repo_id: 33
+ topic_id: 4
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index 54f3ed7b09..c2987b9658 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -407,3 +407,25 @@
lower_name: utf8
name: utf8
is_private: false
+
+-
+ id: 34
+ owner_id: 21
+ lower_name: golang
+ name: golang
+ is_private: false
+ num_stars: 0
+ num_forks: 0
+ num_issues: 0
+ is_mirror: false
+
+-
+ id: 35
+ owner_id: 21
+ lower_name: graphql
+ name: graphql
+ is_private: false
+ num_stars: 0
+ num_forks: 0
+ num_issues: 0
+ is_mirror: false
diff --git a/models/fixtures/topic.yml b/models/fixtures/topic.yml
index b6b94ff4d9..c868b207cb 100644
--- a/models/fixtures/topic.yml
+++ b/models/fixtures/topic.yml
@@ -1,7 +1,7 @@
-
id: 1
name: golang
- repo_count: 1
+ repo_count: 2
-
id: 2
@@ -11,3 +11,7 @@
- id: 3
name: SQL
repo_count: 1
+
+- id: 4
+ name: graphql
+ repo_count: 1
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index 55606e42b9..b3850e3599 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -314,3 +314,18 @@
avatar_email: user20@example.com
num_repos: 4
is_active: true
+
+-
+ id: 21
+ lower_name: user21
+ name: user21
+ full_name: User 21
+ email: user21@example.com
+ passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
+ type: 0 # individual
+ salt: ZogKvWdyEx
+ is_admin: false
+ avatar: avatar21
+ avatar_email: user21@example.com
+ num_repos: 2
+ is_active: true
diff --git a/models/repo_list.go b/models/repo_list.go
index b1527b73c9..c7e292d8a9 100644
--- a/models/repo_list.go
+++ b/models/repo_list.go
@@ -131,6 +131,8 @@ type SearchRepoOptions struct {
// True -> include just mirrors
// False -> include just non-mirrors
Mirror util.OptionalBool
+ // only search topic name
+ TopicOnly bool
}
//SearchOrderBy is used to sort the result
@@ -184,7 +186,7 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
if opts.Collaborate != util.OptionalBoolFalse {
collaborateCond := builder.And(
- builder.Expr("id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID),
+ builder.Expr("repository.id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID),
builder.Neq{"owner_id": opts.OwnerID})
if !opts.Private {
collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false))
@@ -202,7 +204,14 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
}
if opts.Keyword != "" {
- cond = cond.And(builder.Like{"lower_name", strings.ToLower(opts.Keyword)})
+ var keywordCond = builder.NewCond()
+ if opts.TopicOnly {
+ keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)})
+ } else {
+ keywordCond = keywordCond.Or(builder.Like{"lower_name", strings.ToLower(opts.Keyword)})
+ keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)})
+ }
+ cond = cond.And(keywordCond)
}
if opts.Fork != util.OptionalBoolNone {
@@ -224,9 +233,15 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
sess.Join("INNER", "star", "star.repo_id = repository.id")
}
+ if opts.Keyword != "" {
+ sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id")
+ sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id")
+ }
+
count, err := sess.
Where(cond).
Count(new(Repository))
+
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}
@@ -236,11 +251,23 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
sess.Join("INNER", "star", "star.repo_id = repository.id")
}
+ if opts.Keyword != "" {
+ sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id")
+ sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id")
+ }
+
+ if opts.Keyword != "" {
+ sess.Select("repository.*")
+ sess.GroupBy("repository.id")
+ sess.OrderBy("repository." + opts.OrderBy.String())
+ } else {
+ sess.OrderBy(opts.OrderBy.String())
+ }
+
repos := make(RepositoryList, 0, opts.PageSize)
if err = sess.
Where(cond).
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
- OrderBy(opts.OrderBy.String()).
Find(&repos); err != nil {
return nil, 0, fmt.Errorf("Repo: %v", err)
}
diff --git a/models/repo_list_test.go b/models/repo_list_test.go
index 4b5d659ce2..8f4947dbb2 100644
--- a/models/repo_list_test.go
+++ b/models/repo_list_test.go
@@ -147,10 +147,10 @@ func TestSearchRepositoryByName(t *testing.T) {
count: 14},
{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true},
- count: 17},
+ count: 19},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
- count: 21},
+ count: 23},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
count: 13},
@@ -159,7 +159,7 @@ func TestSearchRepositoryByName(t *testing.T) {
count: 11},
{name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse},
- count: 17},
+ count: 19},
}
for _, testCase := range testCases {
@@ -222,3 +222,28 @@ func TestSearchRepositoryByName(t *testing.T) {
})
}
}
+
+func TestSearchRepositoryByTopicName(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ testCases := []struct {
+ name string
+ opts *SearchRepoOptions
+ count int
+ }{
+ {name: "AllPublic/SearchPublicRepositoriesFromTopicAndName",
+ opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"},
+ count: 2},
+ {name: "AllPublic/OnlySearchPublicRepositoriesFromTopic",
+ opts: &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true},
+ count: 1},
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ _, count, err := SearchRepositoryByName(testCase.opts)
+ assert.NoError(t, err)
+ assert.Equal(t, int64(testCase.count), count)
+ })
+ }
+}
diff --git a/models/ssh_key.go b/models/ssh_key.go
index 997e8ee997..9c839755c8 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -18,14 +18,14 @@ import (
"sync"
"time"
- "github.com/Unknwon/com"
- "github.com/go-xorm/xorm"
- "golang.org/x/crypto/ssh"
-
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+
+ "github.com/Unknwon/com"
+ "github.com/go-xorm/xorm"
+ "golang.org/x/crypto/ssh"
)
const (
diff --git a/models/topic_test.go b/models/topic_test.go
index ef374e557b..65e52afb12 100644
--- a/models/topic_test.go
+++ b/models/topic_test.go
@@ -15,7 +15,7 @@ func TestAddTopic(t *testing.T) {
topics, err := FindTopics(&FindTopicOptions{})
assert.NoError(t, err)
- assert.EqualValues(t, 3, len(topics))
+ assert.EqualValues(t, 4, len(topics))
topics, err = FindTopics(&FindTopicOptions{
Limit: 2,
@@ -32,7 +32,7 @@ func TestAddTopic(t *testing.T) {
assert.NoError(t, SaveTopics(2, "golang"))
topics, err = FindTopics(&FindTopicOptions{})
assert.NoError(t, err)
- assert.EqualValues(t, 3, len(topics))
+ assert.EqualValues(t, 4, len(topics))
topics, err = FindTopics(&FindTopicOptions{
RepoID: 2,
@@ -47,7 +47,7 @@ func TestAddTopic(t *testing.T) {
topics, err = FindTopics(&FindTopicOptions{})
assert.NoError(t, err)
- assert.EqualValues(t, 4, len(topics))
+ assert.EqualValues(t, 5, len(topics))
topics, err = FindTopics(&FindTopicOptions{
RepoID: 2,
diff --git a/models/user_test.go b/models/user_test.go
index 20de1a64be..4713b6864c 100644
--- a/models/user_test.go
+++ b/models/user_test.go
@@ -77,13 +77,13 @@ func TestSearchUsers(t *testing.T) {
}
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1},
- []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20})
+ []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21})
testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse},
[]int64{9})
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
- []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20})
+ []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21})
testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 90acde2eea..e48b100af6 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -91,6 +91,7 @@ func Search(ctx *context.APIContext) {
OwnerID: ctx.QueryInt64("uid"),
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
+ TopicOnly: ctx.QueryBool("topic"),
Collaborate: util.OptionalBoolNone,
}
diff --git a/routers/home.go b/routers/home.go
index 0aa907658c..7a23e8765e 100644
--- a/routers/home.go
+++ b/routers/home.go
@@ -122,6 +122,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
}
keyword := strings.Trim(ctx.Query("q"), " ")
+ topicOnly := ctx.QueryBool("topic")
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
Page: page,
@@ -131,6 +132,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
Keyword: keyword,
OwnerID: opts.OwnerID,
AllPublic: true,
+ TopicOnly: topicOnly,
})
if err != nil {
ctx.ServerError("SearchRepositoryByName", err)
diff --git a/routers/user/profile.go b/routers/user/profile.go
index fb731e715c..827226d3e1 100644
--- a/routers/user/profile.go
+++ b/routers/user/profile.go
@@ -105,6 +105,8 @@ func Profile(ctx *context.Context) {
page = 1
}
+ topicOnly := ctx.QueryBool("topic")
+
var (
repos []*models.Repository
count int64
@@ -174,6 +176,7 @@ func Profile(ctx *context.Context) {
PageSize: setting.UI.User.RepoPagingNum,
Starred: true,
Collaborate: util.OptionalBoolFalse,
+ TopicOnly: topicOnly,
})
if err != nil {
ctx.ServerError("SearchRepositoryByName", err)
@@ -217,6 +220,7 @@ func Profile(ctx *context.Context) {
IsProfile: true,
PageSize: setting.UI.User.RepoPagingNum,
Collaborate: util.OptionalBoolFalse,
+ TopicOnly: topicOnly,
})
if err != nil {
ctx.ServerError("SearchRepositoryByName", err)
diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl
index b8f4490c11..e769e4b7fd 100644
--- a/templates/explore/repo_list.tmpl
+++ b/templates/explore/repo_list.tmpl
@@ -20,7 +20,7 @@
{{if .Topics }}
<div>
{{range .Topics}}
- {{if ne . "" }}<div class="ui green basic label topic">{{.}}</div>{{end}}
+ {{if ne . "" }}<a href="/explore/repos?q={{.}}&topic=1"><div class="ui green basic label topic">{{.}}</div></a>{{end}}
{{end}}
</div>
{{end}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index f14f2b9a28..d5a1416cf6 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -24,8 +24,8 @@
{{end}}
</div>
<div class="ui repo-topic" id="repo-topic">
- {{range .Topics}}<div class="ui green basic label topic" style="cursor:pointer;">{{.Name}}</div>{{end}}
- {{if .IsRepositoryAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
+ {{range .Topics}}<a class="ui green basic label topic" style="cursor:pointer;" href="/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
+ {{if .IsRepositoryAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;" href="/explore/repos?q={{.Name}}&topic=1">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
</div>
{{if .IsRepositoryAdmin}}
<div class="ui repo-topic-edit grid form segment error" id="topic_edit" >
@@ -34,7 +34,7 @@
<div class="ui fluid multiple search selection dropdown">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
- <a class="ui green basic label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important;">{{.Name}}<i class="delete icon"></i></a>
+ <a class="ui green basic label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important;" href="/explore/repos?q={{.Name}}&topic=1">{{.Name}}<i class="delete icon"></i></a>
{{end}}
<div class="text"></div>
</div>