@@ -32,6 +32,100 @@ import (
3232 issue_service "code.gitea.io/gitea/services/issue"
3333)
3434
35+ // ErrOwnerOrgRequired represents an error when owner organisation is required for filtering on team
36+ type ErrOwnerOrgRequired struct {}
37+
38+ // IsErrOwnerOrgRequired checks if an error is an ErrOwnerOrgRequired
39+ func IsErrOwnerOrgRequired (err error ) bool {
40+ _ , ok := err .(ErrOwnerOrgRequired )
41+ return ok
42+ }
43+
44+ func (err ErrOwnerOrgRequired ) Error () string {
45+ return "owner organisation is required for filtering on team"
46+ }
47+
48+ // parseIssueIsClosed parses the "state" query parameter and returns the corresponding isClosed option
49+ func parseIssueIsClosed (ctx * context.APIContext ) optional.Option [bool ] {
50+ switch ctx .FormString ("state" ) {
51+ case "closed" :
52+ return optional .Some (true )
53+ case "all" :
54+ return optional .None [bool ]()
55+ default :
56+ return optional .Some (false )
57+ }
58+ }
59+
60+ // parseIssueIsPull parses the "type" query parameter and returns the corresponding isPull option
61+ func parseIssueIsPull (ctx * context.APIContext ) optional.Option [bool ] {
62+ switch ctx .FormString ("type" ) {
63+ case "pulls" :
64+ return optional .Some (true )
65+ case "issues" :
66+ return optional .Some (false )
67+ default :
68+ return optional .None [bool ]()
69+ }
70+ }
71+
72+ // buildSearchIssuesRepoIDs builds the list of repository IDs for issue search based on query parameters.
73+ // It returns repoIDs, allPublic flag, and any error that occurred.
74+ func buildSearchIssuesRepoIDs (ctx * context.APIContext ) ([]int64 , bool , error ) {
75+ var repoIDs []int64
76+ var allPublic bool
77+
78+ opts := repo_model.SearchRepoOptions {
79+ Private : false ,
80+ AllPublic : true ,
81+ TopicOnly : false ,
82+ Collaborate : optional .None [bool ](),
83+ // This needs to be a column that is not nil in fixtures or
84+ // MySQL will return different results when sorting by null in some cases
85+ OrderBy : db .SearchOrderByAlphabetically ,
86+ Actor : ctx .Doer ,
87+ }
88+ if ctx .IsSigned {
89+ opts .Private = ! ctx .PublicOnly
90+ opts .AllLimited = true
91+ }
92+ if ctx .FormString ("owner" ) != "" {
93+ owner , err := user_model .GetUserByName (ctx , ctx .FormString ("owner" ))
94+ if err != nil {
95+ return nil , false , err
96+ }
97+ opts .OwnerID = owner .ID
98+ opts .AllLimited = false
99+ opts .AllPublic = false
100+ opts .Collaborate = optional .Some (false )
101+ }
102+ if ctx .FormString ("team" ) != "" {
103+ if ctx .FormString ("owner" ) == "" {
104+ return nil , false , ErrOwnerOrgRequired {}
105+ }
106+ team , err := organization .GetTeam (ctx , opts .OwnerID , ctx .FormString ("team" ))
107+ if err != nil {
108+ return nil , false , err
109+ }
110+ opts .TeamID = team .ID
111+ }
112+
113+ if opts .AllPublic {
114+ allPublic = true
115+ opts .AllPublic = false // set it false to avoid returning too many repos, we could filter by indexer
116+ }
117+ repoIDs , _ , err := repo_model .SearchRepositoryIDs (ctx , opts )
118+ if err != nil {
119+ return nil , false , err
120+ }
121+ if len (repoIDs ) == 0 {
122+ // no repos found, don't let the indexer return all repos
123+ repoIDs = []int64 {0 }
124+ }
125+
126+ return repoIDs , allPublic , nil
127+ }
128+
35129// SearchIssues searches for issues across the repositories that the user has access to
36130func SearchIssues (ctx * context.APIContext ) {
37131 // swagger:operation GET /repos/issues/search issue issueSearchIssues
@@ -136,97 +230,24 @@ func SearchIssues(ctx *context.APIContext) {
136230 return
137231 }
138232
139- var isClosed optional.Option [bool ]
140- switch ctx .FormString ("state" ) {
141- case "closed" :
142- isClosed = optional .Some (true )
143- case "all" :
144- isClosed = optional .None [bool ]()
145- default :
146- isClosed = optional .Some (false )
147- }
148-
149- var (
150- repoIDs []int64
151- allPublic bool
152- )
153- {
154- // find repos user can access (for issue search)
155- opts := repo_model.SearchRepoOptions {
156- Private : false ,
157- AllPublic : true ,
158- TopicOnly : false ,
159- Collaborate : optional .None [bool ](),
160- // This needs to be a column that is not nil in fixtures or
161- // MySQL will return different results when sorting by null in some cases
162- OrderBy : db .SearchOrderByAlphabetically ,
163- Actor : ctx .Doer ,
164- }
165- if ctx .IsSigned {
166- opts .Private = ! ctx .PublicOnly
167- opts .AllLimited = true
168- }
169- if ctx .FormString ("owner" ) != "" {
170- owner , err := user_model .GetUserByName (ctx , ctx .FormString ("owner" ))
171- if err != nil {
172- if user_model .IsErrUserNotExist (err ) {
173- ctx .APIError (http .StatusBadRequest , err )
174- } else {
175- ctx .APIErrorInternal (err )
176- }
177- return
178- }
179- opts .OwnerID = owner .ID
180- opts .AllLimited = false
181- opts .AllPublic = false
182- opts .Collaborate = optional .Some (false )
183- }
184- if ctx .FormString ("team" ) != "" {
185- if ctx .FormString ("owner" ) == "" {
186- ctx .APIError (http .StatusBadRequest , "Owner organisation is required for filtering on team" )
187- return
188- }
189- team , err := organization .GetTeam (ctx , opts .OwnerID , ctx .FormString ("team" ))
190- if err != nil {
191- if organization .IsErrTeamNotExist (err ) {
192- ctx .APIError (http .StatusBadRequest , err )
193- } else {
194- ctx .APIErrorInternal (err )
195- }
196- return
197- }
198- opts .TeamID = team .ID
199- }
233+ isClosed := parseIssueIsClosed (ctx )
200234
201- if opts .AllPublic {
202- allPublic = true
203- opts .AllPublic = false // set it false to avoid returning too many repos, we could filter by indexer
204- }
205- repoIDs , _ , err = repo_model .SearchRepositoryIDs (ctx , opts )
206- if err != nil {
235+ repoIDs , allPublic , err := buildSearchIssuesRepoIDs (ctx )
236+ if err != nil {
237+ if user_model .IsErrUserNotExist (err ) || organization .IsErrTeamNotExist (err ) || IsErrOwnerOrgRequired (err ) {
238+ ctx .APIError (http .StatusBadRequest , err )
239+ } else {
207240 ctx .APIErrorInternal (err )
208- return
209- }
210- if len (repoIDs ) == 0 {
211- // no repos found, don't let the indexer return all repos
212- repoIDs = []int64 {0 }
213241 }
242+ return
214243 }
215244
216245 keyword := ctx .FormTrim ("q" )
217246 if strings .IndexByte (keyword , 0 ) >= 0 {
218247 keyword = ""
219248 }
220249
221- var isPull optional.Option [bool ]
222- switch ctx .FormString ("type" ) {
223- case "pulls" :
224- isPull = optional .Some (true )
225- case "issues" :
226- isPull = optional .Some (false )
227- default :
228- isPull = optional .None [bool ]()
229- }
250+ isPull := parseIssueIsPull (ctx )
230251
231252 var includedAnyLabels []int64
232253 {
0 commit comments