user := models.AssertExistsAndLoadBean(t, &models.User{ID: 15}).(*models.User)
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 16}).(*models.User)
user3 := models.AssertExistsAndLoadBean(t, &models.User{ID: 18}).(*models.User)
+ user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 20}).(*models.User)
orgUser := models.AssertExistsAndLoadBean(t, &models.User{ID: 17}).(*models.User)
// Map of expected results, where key is user for login
expectedResults
}{
{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{
- nil: {count: 12},
- user: {count: 12},
- user2: {count: 12}},
+ nil: {count: 15},
+ user: {count: 15},
+ user2: {count: 15}},
},
{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{
nil: {count: 10},
user2: {count: 10}},
},
{name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s", "big_test_"), expectedResults: expectedResults{
- nil: {count: 4, repoName: "big_test_"},
- user: {count: 4, repoName: "big_test_"},
- user2: {count: 4, repoName: "big_test_"}},
+ nil: {count: 7, repoName: "big_test_"},
+ user: {count: 7, repoName: "big_test_"},
+ user2: {count: 7, repoName: "big_test_"}},
},
{name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{
nil: {count: 4},
user: {count: 2, repoOwnerID: orgUser.ID, includesPrivate: true},
user2: {count: 1, repoOwnerID: orgUser.ID}},
},
+ {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{
+ nil: {count: 3},
+ user: {count: 3},
+ user4: {count: 6, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{
+ nil: {count: 0},
+ user: {count: 0},
+ user4: {count: 0, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{
+ nil: {count: 1},
+ user: {count: 1},
+ user4: {count: 2, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "fork"), expectedResults: expectedResults{
+ nil: {count: 1},
+ user: {count: 1},
+ user4: {count: 2, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "mirror"), expectedResults: expectedResults{
+ nil: {count: 2},
+ user: {count: 2},
+ user4: {count: 4, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeMirror/Exclusive", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s&exclusive=1", user4.ID, "mirror"), expectedResults: expectedResults{
+ nil: {count: 1},
+ user: {count: 1},
+ user4: {count: 2, includesPrivate: true}}},
+ {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{
+ nil: {count: 0},
+ user: {count: 0},
+ user4: {count: 0, includesPrivate: true}}},
}
for _, testCase := range testCases {
for userToLogin, expected := range testCase.expectedResults {
var session *TestSession
var testName string
+ var userID int64
if userToLogin != nil && userToLogin.ID > 0 {
testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID)
session = loginUser(t, userToLogin.Name)
+ userID = userToLogin.ID
} else {
testName = "AnonymousUser"
session = emptyTestSession(t)
assert.Len(t, body.Data, expected.count)
for _, repo := range body.Data {
+ r := getRepo(t, repo.ID)
+ hasAccess, err := models.HasAccess(userID, r, models.AccessModeRead)
+ assert.NoError(t, err)
+ assert.True(t, hasAccess)
+
assert.NotEmpty(t, repo.Name)
if len(expected.repoName) > 0 {
}
}
+var repoCache = make(map[int64]*models.Repository)
+
+func getRepo(t *testing.T, repoID int64) *models.Repository {
+ if _, ok := repoCache[repoID]; !ok {
+ repoCache[repoID] = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
+ }
+ return repoCache[repoID]
+}
+
func TestAPIViewRepo(t *testing.T) {
prepareTestEnv(t)
id: 11
user_id: 18
repo_id: 21
- mode: 2 # write
\ No newline at end of file
+ mode: 2 # write
+
+-
+ id: 12
+ user_id: 20
+ repo_id: 27
+ mode: 4 # owner
+
+-
+ id: 13
+ user_id: 20
+ repo_id: 28
+ mode: 4 # owner
\ No newline at end of file
org_id: 17
is_public: false
is_owner: true
+ num_teams: 1
+
+-
+ id: 7
+ uid: 20
+ org_id: 19
+ is_public: true
+ is_owner: true
num_teams: 1
\ No newline at end of file
num_closed_pulls: 0
num_watches: 0
is_mirror: false
+ is_fork: false
-
id: 18
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 19
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 20
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 21
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 22
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 23
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
-
id: 24
num_pulls: 0
num_closed_pulls: 0
is_mirror: false
+ is_fork: false
+
+-
+ id: 25
+ owner_id: 20
+ lower_name: big_test_public_mirror_5
+ name: big_test_public_mirror_5
+ is_private: false
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_watches: 0
+ is_mirror: true
+ is_fork: false
+
+-
+ id: 26
+ owner_id: 20
+ lower_name: big_test_private_mirror_5
+ name: big_test_private_mirror_5
+ is_private: true
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_watches: 0
+ is_mirror: true
+ is_fork: false
+
+-
+ id: 27
+ owner_id: 19
+ lower_name: big_test_public_mirror_6
+ name: big_test_public_mirror_6
+ is_private: false
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_watches: 0
+ is_mirror: true
+ num_forks: 1
+ is_fork: false
+
+-
+ id: 28
+ owner_id: 19
+ lower_name: big_test_private_mirror_6
+ name: big_test_private_mirror_6
+ is_private: true
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_watches: 0
+ is_mirror: true
+ num_forks: 1
+ is_fork: false
+
+-
+ id: 29
+ fork_id: 27
+ owner_id: 20
+ lower_name: big_test_public_fork_7
+ name: big_test_public_fork_7
+ is_private: false
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ is_mirror: false
+ is_fork: true
+
+-
+ id: 30
+ fork_id: 28
+ owner_id: 20
+ lower_name: big_test_private_fork_7
+ name: big_test_private_fork_7
+ is_private: true
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ is_mirror: false
+ is_fork: true
\ No newline at end of file
num_repos: 0
num_members: 1
unit_types: '[1,2,3,4,5,6,7]'
+
-
id: 5
org_id: 17
authorize: 4 # owner
num_repos: 2
num_members: 2
+ unit_types: '[1,2,3,4,5,6,7]'
+
+-
+ id: 6
+ org_id: 19
+ lower_name: owners
+ name: Owners
+ authorize: 4 # owner
+ num_repos: 2
+ num_members: 1
unit_types: '[1,2,3,4,5,6,7]'
\ No newline at end of file
id: 5
org_id: 17
team_id: 5
- repo_id: 24
\ No newline at end of file
+ repo_id: 24
+
+-
+ id: 6
+ org_id: 19
+ team_id: 6
+ repo_id: 27
+
+-
+ id: 7
+ org_id: 19
+ team_id: 6
+ repo_id: 28
\ No newline at end of file
id: 7
org_id: 17
team_id: 5
- uid: 18
\ No newline at end of file
+ uid: 18
+
+-
+ id: 8
+ org_id: 19
+ team_id: 6
+ uid: 20
\ No newline at end of file
avatar: avatar18
avatar_email: user18@example.com
num_repos: 0
+ is_active: true
+
+-
+ id: 19
+ lower_name: user19
+ name: user19
+ full_name: User 19
+ email: user19@example.com
+ passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
+ type: 1 # organization
+ salt: ZogKvWdyEx
+ is_admin: false
+ avatar: avatar19
+ avatar_email: user19@example.com
+ num_repos: 2
+ is_active: true
+ num_members: 1
+ num_teams: 1
+
+-
+ id: 20
+ lower_name: user20
+ name: user20
+ full_name: User 20
+ email: user20@example.com
+ passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
+ type: 0 # individual
+ salt: ZogKvWdyEx
+ is_admin: false
+ avatar: avatar20
+ avatar_email: user20@example.com
+ num_repos: 4
is_active: true
\ No newline at end of file
batch := indexer.IssueIndexerBatch()
for page := 1; ; page++ {
repos, _, err := SearchRepositoryByName(&SearchRepoOptions{
- Page: page,
- PageSize: 10,
- OrderBy: SearchOrderByID,
- Private: true,
+ Page: page,
+ PageSize: 10,
+ OrderBy: SearchOrderByID,
+ Private: true,
+ Collaborate: util.OptionalBoolFalse,
})
if err != nil {
return fmt.Errorf("Repositories: %v", err)
"fmt"
"strings"
+ "code.gitea.io/gitea/modules/util"
+
"github.com/go-xorm/builder"
)
}
// SearchRepoOptions holds the search options
-// swagger:parameters repoSearch
type SearchRepoOptions struct {
- // Keyword to search
- //
- // in: query
- Keyword string `json:"q"`
- // Owner in we search search
- //
- // in: query
- OwnerID int64 `json:"uid"`
- OrderBy SearchOrderBy `json:"-"`
- Private bool `json:"-"` // Include private repositories in results
- Collaborate bool `json:"-"` // Include collaborative repositories
- Starred bool `json:"-"`
- Page int `json:"-"`
- IsProfile bool `json:"-"`
- AllPublic bool `json:"-"` // Include also all public repositories
- // Limit of result
- //
- // maximum: setting.ExplorePagingNum
- // in: query
- PageSize int `json:"limit"` // Can be smaller than or equal to setting.ExplorePagingNum
+ Keyword string
+ OwnerID int64
+ OrderBy SearchOrderBy
+ Private bool // Include private repositories in results
+ Starred bool
+ Page int
+ IsProfile bool
+ AllPublic bool // Include also all public repositories
+ PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
+ // None -> include collaborative AND non-collaborative
+ // True -> include just collaborative
+ // False -> incude just non-collaborative
+ Collaborate util.OptionalBool
+ // None -> include forks AND non-forks
+ // True -> include just forks
+ // False -> include just non-forks
+ Fork util.OptionalBool
+ // None -> include mirrors AND non-mirrors
+ // True -> include just mirrors
+ // False -> include just non-mirrors
+ Mirror util.OptionalBool
}
//SearchOrderBy is used to sort the result
cond = cond.And(builder.Eq{"is_private": false})
}
- starred := false
+ var starred bool
if opts.OwnerID > 0 {
if opts.Starred {
starred = true
- cond = builder.Eq{
- "star.uid": opts.OwnerID,
- }
+ cond = builder.Eq{"star.uid": opts.OwnerID}
} else {
- var accessCond builder.Cond = builder.Eq{"owner_id": opts.OwnerID}
+ var accessCond = builder.NewCond()
+ if opts.Collaborate != util.OptionalBoolTrue {
+ accessCond = builder.Eq{"owner_id": opts.OwnerID}
+ }
- if opts.Collaborate {
+ if opts.Collaborate != util.OptionalBoolFalse {
collaborateCond := builder.And(
builder.Expr("id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID),
builder.Neq{"owner_id": opts.OwnerID})
accessCond = accessCond.Or(collaborateCond)
}
+ if opts.AllPublic {
+ accessCond = accessCond.Or(builder.Eq{"is_private": false})
+ }
+
cond = cond.And(accessCond)
}
}
- if opts.OwnerID > 0 && opts.AllPublic {
- cond = cond.Or(builder.Eq{"is_private": false})
- }
-
if opts.Keyword != "" {
cond = cond.And(builder.Like{"lower_name", strings.ToLower(opts.Keyword)})
}
+ if opts.Fork != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
+ }
+
+ if opts.Mirror != util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
+ }
+
if len(opts.OrderBy) == 0 {
opts.OrderBy = SearchOrderByAlphabetically
}
import (
"testing"
+ "code.gitea.io/gitea/modules/util"
+
"github.com/stretchr/testify/assert"
)
// test search public repository on explore page
repos, count, err := SearchRepositoryByName(&SearchRepoOptions{
- Keyword: "repo_12",
- Page: 1,
- PageSize: 10,
+ Keyword: "repo_12",
+ Page: 1,
+ PageSize: 10,
+ Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(1), count)
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
- Keyword: "test_repo",
- Page: 1,
- PageSize: 10,
+ Keyword: "test_repo",
+ Page: 1,
+ PageSize: 10,
+ Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
// test search private repository on explore page
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
- Keyword: "repo_13",
- Page: 1,
- PageSize: 10,
- Private: true,
+ Keyword: "repo_13",
+ Page: 1,
+ PageSize: 10,
+ Private: true,
+ Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(1), count)
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
- Keyword: "test_repo",
- Page: 1,
- PageSize: 10,
- Private: true,
+ Keyword: "test_repo",
+ Page: 1,
+ PageSize: 10,
+ Private: true,
+ Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, repos, 3)
+ // Test non existing owner
+ repos, count, err = SearchRepositoryByName(&SearchRepoOptions{OwnerID: NonexistentID})
+
+ assert.NoError(t, err)
+ assert.Empty(t, repos)
+ assert.Equal(t, int64(0), count)
+
testCases := []struct {
name string
opts *SearchRepoOptions
count int
}{
{name: "PublicRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10},
- count: 4},
+ opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, Collaborate: util.OptionalBoolFalse},
+ count: 7},
{name: "PublicAndPrivateRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true},
- count: 8},
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 5, Private: true},
- count: 8},
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
- opts: &SearchRepoOptions{Keyword: "big_test_", Page: 2, PageSize: 5, Private: true},
- count: 8},
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 2, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
+ {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
+ {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
{name: "PublicRepositoriesOfUser",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
count: 2},
{name: "PublicRepositoriesOfUser2",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
count: 0},
+ {name: "PublicRepositoriesOfUser3",
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
+ count: 2},
{name: "PublicAndPrivateRepositoriesOfUser",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
count: 4},
{name: "PublicAndPrivateRepositoriesOfUser2",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
count: 0},
+ {name: "PublicAndPrivateRepositoriesOfUser3",
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
+ count: 4},
{name: "PublicRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Collaborate: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15},
count: 4},
{name: "PublicRepositoriesOfUser2IncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Collaborate: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18},
count: 1},
+ {name: "PublicRepositoriesOfUser3IncludingCollaborative",
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20},
+ count: 3},
{name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true},
count: 8},
{name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true, Collaborate: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true},
count: 4},
+ {name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative",
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true},
+ count: 6},
{name: "PublicRepositoriesOfOrganization",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
count: 1},
{name: "PublicAndPrivateRepositoriesOfOrganization",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Private: true},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
count: 2},
{name: "AllPublic/PublicRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, AllPublic: true},
- count: 4},
+ opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, AllPublic: true, Collaborate: util.OptionalBoolFalse},
+ count: 7},
{name: "AllPublic/PublicAndPrivateRepositoriesByName",
- opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, AllPublic: true},
- count: 8},
+ opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
+ count: 14},
{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Collaborate: true, AllPublic: true},
- count: 12},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true},
+ count: 15},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: true, AllPublic: true},
- count: 16},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
+ count: 19},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
- opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: true, AllPublic: true},
- count: 10},
+ opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
+ count: 13},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
- opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 18, Private: true, Collaborate: true, AllPublic: true},
- count: 8},
+ opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 18, Private: true, AllPublic: true},
+ count: 11},
{name: "AllPublic/PublicRepositoriesOfOrganization",
- opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true},
- count: 12},
+ opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse},
+ count: 15},
}
for _, testCase := range testCases {
assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count)
- var expectedLen int
- if testCase.opts.PageSize*testCase.opts.Page > testCase.count {
+ page := testCase.opts.Page
+ if page <= 0 {
+ page = 1
+ }
+ var expectedLen = testCase.opts.PageSize
+ if testCase.opts.PageSize*page > testCase.count+testCase.opts.PageSize {
+ expectedLen = 0
+ } else if testCase.opts.PageSize*page > testCase.count {
expectedLen = testCase.count % testCase.opts.PageSize
- } else {
- expectedLen = testCase.opts.PageSize
}
- assert.Len(t, repos, expectedLen)
-
- for _, repo := range repos {
- assert.NotEmpty(t, repo.Name)
-
- if len(testCase.opts.Keyword) > 0 {
- assert.Contains(t, repo.Name, testCase.opts.Keyword)
- }
-
- if testCase.opts.OwnerID > 0 && !testCase.opts.Collaborate && !testCase.opts.AllPublic {
- assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
- }
-
- if !testCase.opts.Private {
- assert.False(t, repo.IsPrivate)
+ if assert.Len(t, repos, expectedLen) {
+ for _, repo := range repos {
+ assert.NotEmpty(t, repo.Name)
+
+ if len(testCase.opts.Keyword) > 0 {
+ assert.Contains(t, repo.Name, testCase.opts.Keyword)
+ }
+
+ if !testCase.opts.Private {
+ assert.False(t, repo.IsPrivate)
+ }
+
+ if testCase.opts.Fork == util.OptionalBoolTrue && testCase.opts.Mirror == util.OptionalBoolTrue {
+ assert.True(t, repo.IsFork || repo.IsMirror)
+ } else {
+ switch testCase.opts.Fork {
+ case util.OptionalBoolFalse:
+ assert.False(t, repo.IsFork)
+ case util.OptionalBoolTrue:
+ assert.True(t, repo.IsFork)
+ }
+
+ switch testCase.opts.Mirror {
+ case util.OptionalBoolFalse:
+ assert.False(t, repo.IsMirror)
+ case util.OptionalBoolTrue:
+ assert.True(t, repo.IsMirror)
+ }
+ }
+
+ if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
+ switch testCase.opts.Collaborate {
+ case util.OptionalBoolFalse:
+ assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
+ case util.OptionalBoolTrue:
+ assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
+ }
+ }
}
}
})
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 2, PageSize: 2},
[]int64{7, 17})
- testOrgSuccess(&SearchUserOptions{Page: 3, PageSize: 2},
+ testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2},
+ []int64{19})
+
+ testOrgSuccess(&SearchUserOptions{Page: 4, PageSize: 2},
[]int64{})
// test users
}
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1},
- []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18})
+ []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20})
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})
+ []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20})
testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
"type": "integer",
"format": "int64",
"x-go-name": "OwnerID",
- "description": "Owner in we search search",
+ "description": "Repository owner to search",
"name": "uid",
"in": "query"
},
"description": "Limit of result\n\nmaximum: setting.ExplorePagingNum",
"name": "limit",
"in": "query"
+ },
+ {
+ "type": "string",
+ "x-go-name": "SearchMode",
+ "description": "Type of repository to search, related to owner",
+ "name": "mode",
+ "in": "query"
+ },
+ {
+ "type": "boolean",
+ "x-go-name": "OwnerExclusive",
+ "description": "Search only owners repositories\nHas effect only if owner is provided and mode is not \"collaborative\"",
+ "name": "exclusive",
+ "in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/SearchResults"
},
+ "422": {
+ "$ref": "#/responses/validationError"
+ },
"500": {
"$ref": "#/responses/SearchError"
}
import (
"fmt"
+ "net/http"
"strings"
api "code.gitea.io/sdk/gitea"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/convert"
)
+// SearchRepoOption options when searching repositories
+// swagger:parameters repoSearch
+type SearchRepoOption struct { // TODO: Move SearchRepoOption to Gitea SDK
+ // Keyword to search
+ //
+ // in: query
+ Keyword string `json:"q"`
+ // Repository owner to search
+ //
+ // in: query
+ OwnerID int64 `json:"uid"`
+ // Limit of result
+ //
+ // maximum: setting.ExplorePagingNum
+ // in: query
+ PageSize int `json:"limit"`
+ // Type of repository to search, related to owner
+ //
+ // in: query
+ SearchMode string `json:"mode"`
+ // Search only owners repositories
+ // Has effect only if owner is provided and mode is not "collaborative"
+ //
+ // in: query
+ OwnerExclusive bool `json:"exclusive"`
+}
+
// Search repositories via options
func Search(ctx *context.APIContext) {
// swagger:route GET /repos/search repository repoSearch
//
// Responses:
// 200: SearchResults
+ // 422: validationError
// 500: SearchError
opts := &models.SearchRepoOptions{
- Keyword: strings.Trim(ctx.Query("q"), " "),
- OwnerID: ctx.QueryInt64("uid"),
- PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
+ Keyword: strings.Trim(ctx.Query("q"), " "),
+ OwnerID: ctx.QueryInt64("uid"),
+ PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
+ Collaborate: util.OptionalBoolNone,
+ }
+
+ if ctx.QueryBool("exclusive") {
+ opts.Collaborate = util.OptionalBoolFalse
+ }
+
+ var mode = ctx.Query("mode")
+ switch mode {
+ case "source":
+ opts.Fork = util.OptionalBoolFalse
+ opts.Mirror = util.OptionalBoolFalse
+ case "fork":
+ opts.Fork = util.OptionalBoolTrue
+ case "mirror":
+ opts.Mirror = util.OptionalBoolTrue
+ case "collaborative":
+ opts.Mirror = util.OptionalBoolFalse
+ opts.Collaborate = util.OptionalBoolTrue
+ case "":
+ default:
+ ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode))
+ return
}
+ var err error
if opts.OwnerID > 0 {
var repoOwner *models.User
if ctx.User != nil && ctx.User.ID == opts.OwnerID {
repoOwner = ctx.User
} else {
- var err error
repoOwner, err = models.GetUserByID(opts.OwnerID)
if err != nil {
ctx.JSON(500, api.SearchError{
}
}
- if !repoOwner.IsOrganization() {
- opts.Collaborate = true
+ if repoOwner.IsOrganization() {
+ opts.Collaborate = util.OptionalBoolFalse
}
// Check visibility.
keyword := strings.Trim(ctx.Query("q"), " ")
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
- Page: page,
- PageSize: opts.PageSize,
- OrderBy: orderBy,
- Private: opts.Private,
- Keyword: keyword,
- OwnerID: opts.OwnerID,
- Collaborate: true,
- AllPublic: true,
+ Page: page,
+ PageSize: opts.PageSize,
+ OrderBy: orderBy,
+ Private: opts.Private,
+ Keyword: keyword,
+ OwnerID: opts.OwnerID,
+ AllPublic: true,
})
if err != nil {
ctx.Handle(500, "SearchRepositoryByName", err)
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/repo"
)
}
} else {
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
- Keyword: keyword,
- OwnerID: ctxUser.ID,
- OrderBy: orderBy,
- Private: showPrivate,
- Page: page,
- PageSize: setting.UI.User.RepoPagingNum,
- Starred: true,
+ Keyword: keyword,
+ OwnerID: ctxUser.ID,
+ OrderBy: orderBy,
+ Private: showPrivate,
+ Page: page,
+ PageSize: setting.UI.User.RepoPagingNum,
+ Starred: true,
+ Collaborate: util.OptionalBoolFalse,
})
if err != nil {
ctx.Handle(500, "SearchRepositoryByName", err)
ctx.Data["Total"] = total
} else {
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
- Keyword: keyword,
- OwnerID: ctxUser.ID,
- OrderBy: orderBy,
- Private: showPrivate,
- Page: page,
- IsProfile: true,
- PageSize: setting.UI.User.RepoPagingNum,
- Collaborate: true,
+ Keyword: keyword,
+ OwnerID: ctxUser.ID,
+ OrderBy: orderBy,
+ Private: showPrivate,
+ Page: page,
+ IsProfile: true,
+ PageSize: setting.UI.User.RepoPagingNum,
})
if err != nil {
ctx.Handle(500, "SearchRepositoryByName", err)