forked from gitea/gitea
		
	Add search mode option to /api/repo/search (#2756)
* Add repo type option to /api/repo/search * Add tests and fix result of collaborative filter in specific condition * Fix/optimize search & tests * Improve integration tests * Fix lint errors * Fix unit tests * Change and improve internal implementation of repo search * Use NonexistentID * Make search api more general * Change mirror and fork search behaviour * Fix tests & typo in comment
This commit is contained in:
		
							parent
							
								
									4d01ecaef3
								
							
						
					
					
						commit
						ddb7f59ef4
					
				| @ -51,6 +51,7 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 	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 | ||||
| @ -66,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 		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}, | ||||
| @ -81,9 +82,9 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 			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}, | ||||
| @ -106,6 +107,34 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 			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 { | ||||
| @ -113,9 +142,11 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 			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) | ||||
| @ -130,6 +161,11 @@ func TestAPISearchRepo(t *testing.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 { | ||||
| @ -150,6 +186,15 @@ func TestAPISearchRepo(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| 
 | ||||
|  | ||||
| @ -62,4 +62,16 @@ | ||||
|   id: 11 | ||||
|   user_id: 18 | ||||
|   repo_id: 21 | ||||
|   mode: 2 # write | ||||
|   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 | ||||
| @ -44,4 +44,12 @@ | ||||
|   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 | ||||
| @ -201,6 +201,7 @@ | ||||
|   num_closed_pulls: 0 | ||||
|   num_watches: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 18 | ||||
| @ -213,6 +214,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 19 | ||||
| @ -225,6 +227,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 20 | ||||
| @ -237,6 +240,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 21 | ||||
| @ -249,6 +253,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 22 | ||||
| @ -261,6 +266,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 23 | ||||
| @ -273,6 +279,7 @@ | ||||
|   num_pulls: 0 | ||||
|   num_closed_pulls: 0 | ||||
|   is_mirror: false | ||||
|   is_fork: false | ||||
| 
 | ||||
| - | ||||
|   id: 24 | ||||
| @ -285,3 +292,90 @@ | ||||
|   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 | ||||
| @ -37,6 +37,7 @@ | ||||
|   num_repos: 0 | ||||
|   num_members: 1 | ||||
|   unit_types: '[1,2,3,4,5,6,7]' | ||||
| 
 | ||||
| - | ||||
|   id: 5 | ||||
|   org_id: 17 | ||||
| @ -45,4 +46,14 @@ | ||||
|   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]' | ||||
| @ -26,4 +26,16 @@ | ||||
|   id: 5 | ||||
|   org_id: 17 | ||||
|   team_id: 5 | ||||
|   repo_id: 24 | ||||
|   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 | ||||
| @ -38,4 +38,10 @@ | ||||
|   id: 7 | ||||
|   org_id: 17 | ||||
|   team_id: 5 | ||||
|   uid: 18 | ||||
|   uid: 18 | ||||
| 
 | ||||
| - | ||||
|   id: 8 | ||||
|   org_id: 19 | ||||
|   team_id: 6 | ||||
|   uid: 20 | ||||
| @ -281,4 +281,36 @@ | ||||
|   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 | ||||
| @ -28,10 +28,11 @@ func populateIssueIndexer() error { | ||||
| 	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) | ||||
|  | ||||
| @ -8,6 +8,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 
 | ||||
| 	"github.com/go-xorm/builder" | ||||
| ) | ||||
| 
 | ||||
| @ -88,28 +90,28 @@ func (repos MirrorRepositoryList) LoadAttributes() error { | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| @ -146,17 +148,18 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | ||||
| 		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}) | ||||
| @ -167,18 +170,26 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | ||||
| 				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 | ||||
| 	} | ||||
|  | ||||
| @ -7,6 +7,8 @@ package models | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| @ -15,9 +17,10 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||
| 
 | ||||
| 	// 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) | ||||
| @ -27,9 +30,10 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||
| 	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) | ||||
| @ -38,10 +42,11 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||
| 
 | ||||
| 	// 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) | ||||
| @ -51,84 +56,110 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||
| 	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 { | ||||
| @ -138,27 +169,54 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, int64(testCase.count), count) | ||||
| 
 | ||||
| 			var expectedLen int | ||||
| 			if testCase.opts.PageSize*testCase.opts.Page > testCase.count { | ||||
| 				expectedLen = testCase.count % testCase.opts.PageSize | ||||
| 			} else { | ||||
| 				expectedLen = testCase.opts.PageSize | ||||
| 			page := testCase.opts.Page | ||||
| 			if page <= 0 { | ||||
| 				page = 1 | ||||
| 			} | ||||
| 			assert.Len(t, repos, expectedLen) | ||||
| 			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 | ||||
| 			} | ||||
| 			if assert.Len(t, repos, expectedLen) { | ||||
| 				for _, repo := range repos { | ||||
| 					assert.NotEmpty(t, repo.Name) | ||||
| 
 | ||||
| 			for _, repo := range repos { | ||||
| 				assert.NotEmpty(t, repo.Name) | ||||
| 					if len(testCase.opts.Keyword) > 0 { | ||||
| 						assert.Contains(t, repo.Name, testCase.opts.Keyword) | ||||
| 					} | ||||
| 
 | ||||
| 				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.OwnerID > 0 && !testCase.opts.Collaborate && !testCase.opts.AllPublic { | ||||
| 					assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID) | ||||
| 				} | ||||
| 					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) | ||||
| 						} | ||||
| 
 | ||||
| 				if !testCase.opts.Private { | ||||
| 					assert.False(t, repo.IsPrivate) | ||||
| 						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) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| @ -63,7 +63,10 @@ func TestSearchUsers(t *testing.T) { | ||||
| 	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 | ||||
| @ -73,13 +76,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}) | ||||
| 		[]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}) | ||||
|  | ||||
							
								
								
									
										19
									
								
								public/swagger.v1.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								public/swagger.v1.json
									
									
									
									
										vendored
									
									
								
							| @ -1102,7 +1102,7 @@ | ||||
|             "type": "integer", | ||||
|             "format": "int64", | ||||
|             "x-go-name": "OwnerID", | ||||
|             "description": "Owner in we search search", | ||||
|             "description": "Repository owner to search", | ||||
|             "name": "uid", | ||||
|             "in": "query" | ||||
|           }, | ||||
| @ -1113,12 +1113,29 @@ | ||||
|             "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" | ||||
|           } | ||||
|  | ||||
| @ -6,6 +6,7 @@ package repo | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	api "code.gitea.io/sdk/gitea" | ||||
| @ -15,9 +16,37 @@ import ( | ||||
| 	"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 | ||||
| @ -27,20 +56,44 @@ func Search(ctx *context.APIContext) { | ||||
| 	// | ||||
| 	//     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{ | ||||
| @ -51,8 +104,8 @@ func Search(ctx *context.APIContext) { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if !repoOwner.IsOrganization() { | ||||
| 			opts.Collaborate = true | ||||
| 		if repoOwner.IsOrganization() { | ||||
| 			opts.Collaborate = util.OptionalBoolFalse | ||||
| 		} | ||||
| 
 | ||||
| 		// Check visibility. | ||||
|  | ||||
| @ -108,14 +108,13 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | ||||
| 	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) | ||||
|  | ||||
| @ -15,6 +15,7 @@ import ( | ||||
| 	"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" | ||||
| ) | ||||
| 
 | ||||
| @ -157,13 +158,14 @@ func Profile(ctx *context.Context) { | ||||
| 			} | ||||
| 		} 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) | ||||
| @ -199,14 +201,13 @@ func Profile(ctx *context.Context) { | ||||
| 			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) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Morlinest
						Morlinest