diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index d5b1676d6eac3..c13b1422e5b70 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -6,7 +6,6 @@ package integrations import ( "net/http" - "strings" "testing" "code.gitea.io/gitea/models" @@ -33,11 +32,6 @@ func TestAPIUserReposNotLogin(t *testing.T) { } } -type searchResponseBody struct { - ok bool - data []api.Repository -} - func TestAPISearchRepoNotLogin(t *testing.T) { prepareTestEnv(t) const keyword = "test" @@ -45,10 +39,226 @@ func TestAPISearchRepoNotLogin(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword) resp := MakeRequest(t, req, http.StatusOK) - var body searchResponseBody + var body api.SearchResults + DecodeJSON(t, resp, &body) + assert.NotEmpty(t, body.Data) + for _, repo := range body.Data { + assert.Contains(t, repo.Name, keyword) + assert.False(t, repo.Private) + } + + // Should return all (max 50) public repositories + req = NewRequest(t, "GET", "/api/v1/repos/search?limit=50") + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 12) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Should return (max 10) public repositories + req = NewRequest(t, "GET", "/api/v1/repos/search?limit=10") + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &body) - for _, repo := range body.data { - assert.True(t, strings.Contains(repo.Name, keyword)) + assert.Len(t, body.Data, 10) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + const keyword2 = "big_test_" + // Should return all public repositories which (partial) match keyword + req = NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword2) + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 4) + for _, repo := range body.Data { + assert.Contains(t, repo.Name, keyword2) + assert.False(t, repo.Private) + } + + // Should return all public repositories accessible and related to user + const userID = int64(15) + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", userID) + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 4) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Should return all public repositories accessible and related to user + const user2ID = int64(16) + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", user2ID) + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 1) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Should return all public repositories owned by organization + const orgID = int64(17) + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", orgID) + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 1) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.Equal(t, repo.Owner.ID, orgID) + assert.False(t, repo.Private) + } +} + +func TestAPISearchRepoLoggedUser(t *testing.T) { + prepareTestEnv(t) + + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 15}).(*models.User) + user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 16}).(*models.User) + session := loginUser(t, user.Name) + session2 := loginUser(t, user2.Name) + + var body api.SearchResults + + // Get public repositories accessible and not related to logged in user that match the keyword + // Should return all public repositories which (partial) match keyword + const keyword = "big_test_" + req := NewRequestf(t, "GET", "/api/v1/repos/search?q=%s", keyword) + resp := session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 4) + for _, repo := range body.Data { + assert.Contains(t, repo.Name, keyword) + assert.False(t, repo.Private) + } + // Test when user2 is logged in + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 4) + for _, repo := range body.Data { + assert.Contains(t, repo.Name, keyword) + assert.False(t, repo.Private) + } + + // Get all public repositories accessible and not related to logged in user + // Should return all (max 50) public repositories + req = NewRequest(t, "GET", "/api/v1/repos/search?limit=50") + resp = session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 12) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + // Test when user2 is logged in + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 12) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Get all public repositories accessible and not related to logged in user + // Should return all (max 10) public repositories + req = NewRequest(t, "GET", "/api/v1/repos/search?limit=10") + resp = session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 10) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + // Test when user2 is logged in + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 10) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Get repositories of logged in user + // Should return all public and private repositories accessible and related to user + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", user.ID) + resp = session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 8) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + } + // Test when user2 is logged in + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", user2.ID) + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 2) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + } + + // Get repositories of another user + // Should return all public repositories accessible and related to user + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", user2.ID) + resp = session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 1) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + // Test when user2 is logged in + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", user.ID) + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 4) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.False(t, repo.Private) + } + + // Get repositories of organization owned by logged in user + // Should return all public and private repositories owned by organization + const orgID = int64(17) + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", orgID) + resp = session.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 2) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.Equal(t, repo.Owner.ID, orgID) + } + + // Get repositories of organization owned by another user + // Should return all public repositories owned by organization + req = NewRequestf(t, "GET", "/api/v1/repos/search?uid=%d", orgID) + resp = session2.MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &body) + assert.Len(t, body.Data, 1) + for _, repo := range body.Data { + assert.NotEmpty(t, repo.Name) + assert.Equal(t, repo.Owner.ID, orgID) + assert.False(t, repo.Private) } } diff --git a/models/fixtures/access.yml b/models/fixtures/access.yml index 8722b248db2fb..e69683a7cdf6d 100644 --- a/models/fixtures/access.yml +++ b/models/fixtures/access.yml @@ -15,3 +15,27 @@ user_id: 4 repo_id: 3 mode: 2 # write + +- + id: 4 + user_id: 15 + repo_id: 22 + mode: 2 # write + +- + id: 5 + user_id: 15 + repo_id: 21 + mode: 2 # write + +- + id: 6 + user_id: 15 + repo_id: 23 + mode: 4 # owner + +- + id: 7 + user_id: 15 + repo_id: 24 + mode: 4 # owner diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml index a7b8d178aa13a..c7fe2cf369522 100644 --- a/models/fixtures/org_user.yml +++ b/models/fixtures/org_user.yml @@ -29,3 +29,11 @@ is_public: false is_owner: true num_teams: 1 + +- + id: 5 + uid: 15 + org_id: 17 + is_public: true + is_owner: true + num_teams: 1 diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index b8f607b2a8ec5..8c8afb27b083e 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -188,3 +188,100 @@ num_pulls: 0 num_closed_pulls: 0 num_watches: 0 + +- + id: 17 + owner_id: 15 + lower_name: big_test_public_1 + name: big_test_public_1 + is_private: false + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + num_watches: 0 + is_mirror: false + +- + id: 18 + owner_id: 15 + lower_name: big_test_public_2 + name: big_test_public_2 + is_private: false + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 19 + owner_id: 15 + lower_name: big_test_private_1 + name: big_test_private_1 + is_private: true + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 20 + owner_id: 15 + lower_name: big_test_private_2 + name: big_test_private_2 + is_private: true + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 21 + owner_id: 16 + lower_name: big_test_public_3 + name: big_test_public_3 + is_private: false + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 22 + owner_id: 16 + lower_name: big_test_private_3 + name: big_test_private_3 + is_private: true + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 23 + owner_id: 17 + lower_name: big_test_public_4 + name: big_test_public_4 + is_private: false + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false + +- + id: 24 + owner_id: 17 + lower_name: big_test_private_4 + name: big_test_private_4 + is_private: true + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_mirror: false diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index 795d5cda6c998..298de648ddf78 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -37,3 +37,12 @@ num_repos: 0 num_members: 1 unit_types: '[1,2,3,4,5,6,7]' +- + id: 5 + org_id: 17 + lower_name: owners + name: Owners + authorize: 4 # owner + num_repos: 2 + num_members: 1 + unit_types: '[1,2,3,4,5,6,7]' diff --git a/models/fixtures/team_repo.yml b/models/fixtures/team_repo.yml index 1f7385e7e9c17..5154453f7b4dd 100644 --- a/models/fixtures/team_repo.yml +++ b/models/fixtures/team_repo.yml @@ -15,3 +15,15 @@ org_id: 3 team_id: 1 repo_id: 5 + +- + id: 4 + org_id: 17 + team_id: 5 + repo_id: 23 + +- + id: 5 + org_id: 17 + team_id: 5 + repo_id: 24 \ No newline at end of file diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml index 955a80e4cdf03..8d9d9203782e4 100644 --- a/models/fixtures/team_user.yml +++ b/models/fixtures/team_user.yml @@ -27,3 +27,9 @@ org_id: 7 team_id: 4 uid: 5 + +- + id: 6 + org_id: 17 + team_id: 5 + uid: 15 \ No newline at end of file diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index abd72b1168105..e1fbc9e578bfe 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -218,3 +218,50 @@ avatar_email: user13@example.com num_repos: 3 is_active: true + +- + id: 15 + lower_name: user15 + name: user15 + full_name: User 15 + email: user15@example.com + passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password + type: 0 # individual + salt: ZogKvWdyEx + is_admin: false + avatar: avatar15 + avatar_email: user15@example.com + num_repos: 4 + is_active: true + +- + id: 16 + lower_name: user16 + name: user16 + full_name: User 16 + email: user16@example.com + passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password + type: 0 # individual + salt: ZogKvWdyEx + is_admin: false + avatar: avatar16 + avatar_email: user16@example.com + num_repos: 2 + is_active: true + +- + id: 17 + lower_name: user17 + name: user17 + full_name: User 17 + email: user17@example.com + passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password + type: 1 # organization + salt: ZogKvWdyEx + is_admin: false + avatar: avatar17 + avatar_email: user17@example.com + num_repos: 2 + is_active: true + num_members: 1 + num_teams: 1 \ No newline at end of file diff --git a/models/org_test.go b/models/org_test.go index 07da2d56af906..c7bdb8b5d3dbb 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -252,7 +252,7 @@ func TestOrganizations(t *testing.T) { []int64{3, 6}) testSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 2, PageSize: 2}, - []int64{7}) + []int64{7, 17}) testSuccess(&SearchUserOptions{Page: 3, PageSize: 2}, []int64{}) diff --git a/models/repo_list.go b/models/repo_list.go index 15e6144d06366..1e2fa8c6693da 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -97,14 +97,13 @@ type SearchRepoOptions struct { // Owner in we search search // // in: query - OwnerID int64 `json:"uid"` - Searcher *User `json:"-"` //ID of the person who's seeking - OrderBy string `json:"-"` - Private bool `json:"-"` // Include private repositories in results - Collaborate bool `json:"-"` // Include collaborative repositories - Starred bool `json:"-"` - Page int `json:"-"` - IsProfile bool `json:"-"` + 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:"-"` // Limit of result // // maximum: setting.ExplorePagingNum @@ -112,88 +111,111 @@ type SearchRepoOptions struct { PageSize int `json:"limit"` // Can be smaller than or equal to setting.ExplorePagingNum } +//SearchOrderBy is used to sort the result +type SearchOrderBy string + +func (s SearchOrderBy) String() string { + return string(s) +} + +// Strings for sorting result +const ( + SearchOrderByAlphabetically SearchOrderBy = "name ASC" + SearchOrderByAlphabeticallyReverse = "name DESC" + SearchOrderByLeastUpdated = "updated_unix ASC" + SearchOrderByRecentUpdated = "updated_unix DESC" + SearchOrderByOldest = "created_unix ASC" + SearchOrderByNewest = "created_unix DESC" + SearchOrderBySize = "size ASC" + SearchOrderBySizeReverse = "size DESC" +) + // SearchRepositoryByName takes keyword and part of repository name to search, // it returns results in given range and number of total results. -func SearchRepositoryByName(opts *SearchRepoOptions) (repos RepositoryList, count int64, err error) { - var cond = builder.NewCond() +func SearchRepositoryByName(opts *SearchRepoOptions) (repos RepositoryList, _ int64, _ error) { + // Check if user with Owner ID exists + if opts.OwnerID > 0 { + userExists, err := GetUser(&User{ID: opts.OwnerID}) + if err != nil { + return nil, 0, err + } + if !userExists { + return nil, 0, ErrUserNotExist{UID: opts.OwnerID} + } + } + + // Check and set page to correct number if opts.Page <= 0 { opts.Page = 1 } - var starJoin bool - if opts.Starred && opts.OwnerID > 0 { - cond = builder.Eq{ - "star.uid": opts.OwnerID, - } - starJoin = true - } + var cond = builder.NewCond() - opts.Keyword = strings.ToLower(opts.Keyword) + // Add repository name keyword to search for if opts.Keyword != "" { + opts.Keyword = strings.ToLower(opts.Keyword) cond = cond.And(builder.Like{"lower_name", opts.Keyword}) } - // Append conditions - if !opts.Starred && opts.OwnerID > 0 { - var searcherReposCond builder.Cond = builder.Eq{"owner_id": opts.OwnerID} - if opts.Searcher != nil { - var ownerIds []int64 - - ownerIds = append(ownerIds, opts.Searcher.ID) - err = opts.Searcher.GetOrganizations(true) - - if err != nil { - return nil, 0, fmt.Errorf("Organization: %v", err) - } + // Exclude private repositories + if !opts.Private { + cond = cond.And(builder.Eq{"is_private": false}) + } - for _, org := range opts.Searcher.Orgs { - ownerIds = append(ownerIds, org.ID) + includeStarred := false + if opts.OwnerID > 0 { + if opts.Starred { + // Return only starred repositories by Owner + includeStarred = true + cond = builder.Eq{ + "star.uid": opts.OwnerID, } + } else { + // Set user access conditions + // Add Owner ID to access conditions + var accessCond builder.Cond = builder.Eq{"owner_id": opts.OwnerID} - searcherReposCond = searcherReposCond.Or(builder.In("owner_id", ownerIds)) + // Include collaborative repositories if opts.Collaborate { - searcherReposCond = searcherReposCond.Or(builder.Expr("id IN (SELECT repo_id FROM `access` WHERE access.user_id = ? AND owner_id != ?)", - opts.Searcher.ID, opts.Searcher.ID)) + // Add repositories where user is set as collaborator directly + accessCond = accessCond.Or(builder.And( + builder.Expr("id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID), + builder.Neq{"owner_id": opts.OwnerID})) } - } - cond = cond.And(searcherReposCond) - } - if !opts.Private { - cond = cond.And(builder.Eq{"is_private": false}) + // Add user access conditions to search + cond = cond.And(accessCond) + } } if len(opts.OrderBy) == 0 { - opts.OrderBy = "name ASC" + opts.OrderBy = SearchOrderByAlphabetically } sess := x.NewSession() defer sess.Close() - if starJoin { - count, err = sess. - Join("INNER", "star", "star.repo_id = repository.id"). - Where(cond). - Count(new(Repository)) - if err != nil { - return nil, 0, fmt.Errorf("Count: %v", err) - } + if includeStarred { + sess.Join("INNER", "star", "star.repo_id = repository.id") + } + count, err := sess. + Where(cond). + Count(new(Repository)) + if err != nil { + return nil, 0, fmt.Errorf("Count: %v", err) + } + + // Set again after reset by Count() + if includeStarred { sess.Join("INNER", "star", "star.repo_id = repository.id") - } else { - count, err = sess. - Where(cond). - Count(new(Repository)) - if err != nil { - return nil, 0, fmt.Errorf("Count: %v", err) - } } repos = make([]*Repository, 0, opts.PageSize) if err = sess. Where(cond). Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). - OrderBy(opts.OrderBy). + OrderBy(opts.OrderBy.String()). Find(&repos); err != nil { return nil, 0, fmt.Errorf("Repo: %v", err) } @@ -204,7 +226,7 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (repos RepositoryList, coun } } - return + return repos, count, nil } // Repositories returns all repositories @@ -217,7 +239,7 @@ func Repositories(opts *SearchRepoOptions) (_ RepositoryList, count int64, err e if err = x. Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). - OrderBy(opts.OrderBy). + OrderBy(opts.OrderBy.String()). Find(&repos); err != nil { return nil, 0, fmt.Errorf("Repo: %v", err) } @@ -230,54 +252,3 @@ func Repositories(opts *SearchRepoOptions) (_ RepositoryList, count int64, err e return repos, count, nil } - -// GetRecentUpdatedRepositories returns the list of repositories that are recently updated. -func GetRecentUpdatedRepositories(opts *SearchRepoOptions) (repos RepositoryList, _ int64, _ error) { - var cond = builder.NewCond() - - if len(opts.OrderBy) == 0 { - opts.OrderBy = "updated_unix DESC" - } - - if !opts.Private { - cond = builder.Eq{ - "is_private": false, - } - } - - if opts.Searcher != nil && !opts.Searcher.IsAdmin { - var ownerIds []int64 - - ownerIds = append(ownerIds, opts.Searcher.ID) - err := opts.Searcher.GetOrganizations(true) - - if err != nil { - return nil, 0, fmt.Errorf("Organization: %v", err) - } - - for _, org := range opts.Searcher.Orgs { - ownerIds = append(ownerIds, org.ID) - } - - cond = cond.Or(builder.In("owner_id", ownerIds)) - } - - count, err := x.Where(cond).Count(new(Repository)) - if err != nil { - return nil, 0, fmt.Errorf("Count: %v", err) - } - - if err = x.Where(cond). - Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). - Limit(opts.PageSize). - OrderBy(opts.OrderBy). - Find(&repos); err != nil { - return nil, 0, fmt.Errorf("Repo: %v", err) - } - - if err = repos.loadAttributes(x); err != nil { - return nil, 0, fmt.Errorf("LoadAttributes: %v", err) - } - - return repos, count, nil -} diff --git a/models/repo_list_test.go b/models/repo_list_test.go index 8ac0d804892da..04f8bbaa28824 100644 --- a/models/repo_list_test.go +++ b/models/repo_list_test.go @@ -18,10 +18,8 @@ func TestSearchRepositoryByName(t *testing.T) { Keyword: "repo_12", Page: 1, PageSize: 10, - Searcher: nil, }) - assert.NotNil(t, repos) assert.NoError(t, err) if assert.Len(t, repos, 1) { assert.Equal(t, "test_repo_12", repos[0].Name) @@ -32,12 +30,11 @@ func TestSearchRepositoryByName(t *testing.T) { Keyword: "test_repo", Page: 1, PageSize: 10, - Searcher: nil, }) - assert.NotNil(t, repos) assert.NoError(t, err) assert.Equal(t, int64(2), count) + assert.Len(t, repos, 2) // test search private repository on explore page repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ @@ -45,10 +42,8 @@ func TestSearchRepositoryByName(t *testing.T) { Page: 1, PageSize: 10, Private: true, - Searcher: &User{ID: 14}, }) - assert.NotNil(t, repos) assert.NoError(t, err) if assert.Len(t, repos, 1) { assert.Equal(t, "test_repo_13", repos[0].Name) @@ -60,10 +55,127 @@ func TestSearchRepositoryByName(t *testing.T) { Page: 1, PageSize: 10, Private: true, - Searcher: &User{ID: 14}, }) - assert.NotNil(t, repos) assert.NoError(t, err) assert.Equal(t, int64(3), count) + assert.Len(t, repos, 3) + + // Get all public repositories by name + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Keyword: "big_test_", + Page: 1, + PageSize: 10, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(4), count) + assert.Len(t, repos, 4) + + // Get all public + private repositories by name + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Keyword: "big_test_", + Page: 1, + PageSize: 10, + Private: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(8), count) + assert.Len(t, repos, 8) + + // Get all public + private repositories by name with pagesize limit (first page) + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Keyword: "big_test_", + Page: 1, + PageSize: 5, + Private: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(8), count) + assert.Len(t, repos, 5) + + // Get all public + private repositories by name with pagesize limit (second page) + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Keyword: "big_test_", + Page: 2, + PageSize: 5, + Private: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(8), count) + assert.Len(t, repos, 3) + + // Get all public repositories of user + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 15, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(2), count) + assert.Len(t, repos, 2) + + // Get all public + private repositories of user + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 15, + Private: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(4), count) + assert.Len(t, repos, 4) + + // Get all public (including collaborative) repositories of user + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 15, + Collaborate: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(4), count) + assert.Len(t, repos, 4) + + // Get all public + private (including collaborative) repositories of user + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 15, + Private: true, + Collaborate: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(8), count) + assert.Len(t, repos, 8) + + // Get all public repositories of organization + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 17, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(1), count) + assert.Len(t, repos, 1) + + // Get all public + private repositories of organization + repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ + Page: 1, + PageSize: 10, + OwnerID: 17, + Private: true, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(2), count) + assert.Len(t, repos, 2) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 20393102fc825..eef2d0604c91f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -34,31 +34,25 @@ func Search(ctx *context.APIContext) { OwnerID: ctx.QueryInt64("uid"), PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), } - if ctx.User != nil && ctx.User.ID == opts.OwnerID { - opts.Searcher = ctx.User - } - // Check visibility. - if ctx.IsSigned && opts.OwnerID > 0 { - if ctx.User.ID == opts.OwnerID { - opts.Private = true + // Include collaborative and private repositories + if opts.OwnerID > 0 { + owner, err := models.GetUserByID(opts.OwnerID) + if err != nil { + ctx.JSON(500, api.SearchError{ + OK: false, + Error: err.Error(), + }) + return + } + + if !owner.IsOrganization() { opts.Collaborate = true - } else { - u, err := models.GetUserByID(opts.OwnerID) - if err != nil { - ctx.JSON(500, api.SearchError{ - OK: false, - Error: err.Error(), - }) - return - } - if u.IsOrganization() && u.IsOwnedBy(ctx.User.ID) { - opts.Private = true - } + } - if !u.IsOrganization() { - opts.Collaborate = true - } + // Check visibility. + if ctx.IsSigned && (ctx.User.ID == owner.ID || (owner.IsOrganization() && owner.IsOwnedBy(ctx.User.ID))) { + opts.Private = true } } diff --git a/routers/home.go b/routers/home.go index 16d0720551c40..13060d716ca8d 100644 --- a/routers/home.go +++ b/routers/home.go @@ -61,7 +61,6 @@ func Swagger(ctx *context.Context) { // RepoSearchOptions when calling search repositories type RepoSearchOptions struct { Ranger func(*models.SearchRepoOptions) (models.RepositoryList, int64, error) - Searcher *models.User Private bool PageSize int TplName base.TplName @@ -86,61 +85,45 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { repos []*models.Repository count int64 err error - orderBy string + orderBy models.SearchOrderBy ) ctx.Data["SortType"] = ctx.Query("sort") switch ctx.Query("sort") { case "oldest": - orderBy = "created_unix ASC" + orderBy = models.SearchOrderByOldest case "recentupdate": - orderBy = "updated_unix DESC" + orderBy = models.SearchOrderByRecentUpdated case "leastupdate": - orderBy = "updated_unix ASC" + orderBy = models.SearchOrderByLeastUpdated case "reversealphabetically": - orderBy = "name DESC" + orderBy = models.SearchOrderByAlphabeticallyReverse case "alphabetically": - orderBy = "name ASC" + orderBy = models.SearchOrderByAlphabetically case "reversesize": - orderBy = "size DESC" + orderBy = models.SearchOrderBySizeReverse case "size": - orderBy = "size ASC" + orderBy = models.SearchOrderBySize default: - orderBy = "created_unix DESC" + orderBy = models.SearchOrderByNewest } - keyword := strings.Trim(ctx.Query("q"), " ") - if len(keyword) == 0 { - repos, count, err = opts.Ranger(&models.SearchRepoOptions{ - Page: page, - PageSize: opts.PageSize, - Searcher: ctx.User, - OrderBy: orderBy, - Private: opts.Private, - Collaborate: true, - }) + searchOpts := &models.SearchRepoOptions{ + Page: page, + PageSize: opts.PageSize, + OrderBy: orderBy, + Private: opts.Private, + Keyword: strings.Trim(ctx.Query("q"), " "), + } + + if len(searchOpts.Keyword) == 0 || isKeywordValid(searchOpts.Keyword) { + repos, count, err = models.SearchRepositoryByName(searchOpts) if err != nil { - ctx.Handle(500, "opts.Ranger", err) + ctx.Handle(500, "SearchRepositoryByName", err) return } - } else { - if isKeywordValid(keyword) { - repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ - Keyword: keyword, - OrderBy: orderBy, - Private: opts.Private, - Page: page, - PageSize: opts.PageSize, - Searcher: ctx.User, - Collaborate: true, - }) - if err != nil { - ctx.Handle(500, "SearchRepositoryByName", err) - return - } - } } - ctx.Data["Keyword"] = keyword + ctx.Data["Keyword"] = searchOpts.Keyword ctx.Data["Total"] = count ctx.Data["Page"] = paginater.New(int(count), opts.PageSize, page, 5) ctx.Data["Repos"] = repos @@ -155,9 +138,7 @@ func ExploreRepos(ctx *context.Context) { ctx.Data["PageIsExploreRepositories"] = true RenderRepoSearch(ctx, &RepoSearchOptions{ - Ranger: models.GetRecentUpdatedRepositories, PageSize: setting.UI.ExplorePagingNum, - Searcher: ctx.User, Private: ctx.User != nil && ctx.User.IsAdmin, TplName: tplExploreRepos, }) diff --git a/routers/user/profile.go b/routers/user/profile.go index 23f5057727cab..50d0c2397fbbb 100644 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -107,26 +107,26 @@ func Profile(ctx *context.Context) { var ( repos []*models.Repository count int64 - orderBy string + orderBy models.SearchOrderBy ) ctx.Data["SortType"] = ctx.Query("sort") switch ctx.Query("sort") { case "newest": - orderBy = "created_unix DESC" + orderBy = models.SearchOrderByNewest case "oldest": - orderBy = "created_unix ASC" + orderBy = models.SearchOrderByOldest case "recentupdate": - orderBy = "updated_unix DESC" + orderBy = models.SearchOrderByRecentUpdated case "leastupdate": - orderBy = "updated_unix ASC" + orderBy = models.SearchOrderByLeastUpdated case "reversealphabetically": - orderBy = "name DESC" + orderBy = models.SearchOrderByAlphabeticallyReverse case "alphabetically": - orderBy = "name ASC" + orderBy = models.SearchOrderByAlphabetically default: ctx.Data["SortType"] = "recentupdate" - orderBy = "updated_unix DESC" + orderBy = models.SearchOrderByNewest } // set default sort value if sort is empty. @@ -149,7 +149,7 @@ func Profile(ctx *context.Context) { case "stars": ctx.Data["PageIsProfileStarList"] = true if len(keyword) == 0 { - repos, err = ctxUser.GetStarredRepos(showPrivate, page, setting.UI.User.RepoPagingNum, orderBy) + repos, err = ctxUser.GetStarredRepos(showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String()) if err != nil { ctx.Handle(500, "GetStarredRepos", err) return @@ -182,7 +182,7 @@ func Profile(ctx *context.Context) { default: if len(keyword) == 0 { var total int - repos, err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy) + repos, err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String()) if err != nil { ctx.Handle(500, "GetRepositories", err) return