Skip to content

Commit 2aeb966

Browse files
committed
refactor: extract helper functions from SearchIssues
Split the SearchIssues function into smaller, more maintainable pieces: - parseIssueIsClosed: parses "state" query parameter - parseIssueIsPull: parses "type" query parameter - buildSearchIssuesRepoIDs: builds repository IDs list for search This improves code readability and makes the logic easier to test and reuse. Ref: #35015
1 parent 1f5237e commit 2aeb966

File tree

1 file changed

+93
-81
lines changed

1 file changed

+93
-81
lines changed

routers/api/v1/repo/issue.go

Lines changed: 93 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,87 @@ import (
3232
issue_service "code.gitea.io/gitea/services/issue"
3333
)
3434

35+
// parseIssueIsClosed parses the "state" query parameter and returns the corresponding isClosed option
36+
func parseIssueIsClosed(ctx *context.APIContext) optional.Option[bool] {
37+
switch ctx.FormString("state") {
38+
case "closed":
39+
return optional.Some(true)
40+
case "all":
41+
return optional.None[bool]()
42+
default:
43+
return optional.Some(false)
44+
}
45+
}
46+
47+
// parseIssueIsPull parses the "type" query parameter and returns the corresponding isPull option
48+
func parseIssueIsPull(ctx *context.APIContext) optional.Option[bool] {
49+
switch ctx.FormString("type") {
50+
case "pulls":
51+
return optional.Some(true)
52+
case "issues":
53+
return optional.Some(false)
54+
default:
55+
return optional.None[bool]()
56+
}
57+
}
58+
59+
// buildSearchIssuesRepoIDs builds the list of repository IDs for issue search based on query parameters.
60+
// It returns repoIDs, allPublic flag, and any error that occurred.
61+
func buildSearchIssuesRepoIDs(ctx *context.APIContext) ([]int64, bool, error) {
62+
var repoIDs []int64
63+
var allPublic bool
64+
65+
opts := repo_model.SearchRepoOptions{
66+
Private: false,
67+
AllPublic: true,
68+
TopicOnly: false,
69+
Collaborate: optional.None[bool](),
70+
// This needs to be a column that is not nil in fixtures or
71+
// MySQL will return different results when sorting by null in some cases
72+
OrderBy: db.SearchOrderByAlphabetically,
73+
Actor: ctx.Doer,
74+
}
75+
if ctx.IsSigned {
76+
opts.Private = !ctx.PublicOnly
77+
opts.AllLimited = true
78+
}
79+
if ctx.FormString("owner") != "" {
80+
owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
81+
if err != nil {
82+
return nil, false, err
83+
}
84+
opts.OwnerID = owner.ID
85+
opts.AllLimited = false
86+
opts.AllPublic = false
87+
opts.Collaborate = optional.Some(false)
88+
}
89+
if ctx.FormString("team") != "" {
90+
if ctx.FormString("owner") == "" {
91+
return nil, false, fmt.Errorf("owner organisation is required for filtering on team")
92+
}
93+
team, err := organization.GetTeam(ctx, opts.OwnerID, ctx.FormString("team"))
94+
if err != nil {
95+
return nil, false, err
96+
}
97+
opts.TeamID = team.ID
98+
}
99+
100+
if opts.AllPublic {
101+
allPublic = true
102+
opts.AllPublic = false // set it false to avoid returning too many repos, we could filter by indexer
103+
}
104+
repoIDs, _, err := repo_model.SearchRepositoryIDs(ctx, opts)
105+
if err != nil {
106+
return nil, false, err
107+
}
108+
if len(repoIDs) == 0 {
109+
// no repos found, don't let the indexer return all repos
110+
repoIDs = []int64{0}
111+
}
112+
113+
return repoIDs, allPublic, nil
114+
}
115+
35116
// SearchIssues searches for issues across the repositories that the user has access to
36117
func SearchIssues(ctx *context.APIContext) {
37118
// swagger:operation GET /repos/issues/search issue issueSearchIssues
@@ -136,97 +217,28 @@ func SearchIssues(ctx *context.APIContext) {
136217
return
137218
}
138219

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-
}
220+
isClosed := parseIssueIsClosed(ctx)
200221

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 {
222+
repoIDs, allPublic, err := buildSearchIssuesRepoIDs(ctx)
223+
if err != nil {
224+
if user_model.IsErrUserNotExist(err) {
225+
ctx.APIError(http.StatusBadRequest, err)
226+
} else if organization.IsErrTeamNotExist(err) {
227+
ctx.APIError(http.StatusBadRequest, err)
228+
} else if err.Error() == "owner organisation is required for filtering on team" {
229+
ctx.APIError(http.StatusBadRequest, err)
230+
} else {
207231
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}
213232
}
233+
return
214234
}
215235

216236
keyword := ctx.FormTrim("q")
217237
if strings.IndexByte(keyword, 0) >= 0 {
218238
keyword = ""
219239
}
220240

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-
}
241+
isPull := parseIssueIsPull(ctx)
230242

231243
var includedAnyLabels []int64
232244
{

0 commit comments

Comments
 (0)