Merge branch 'master' into add-api-issues-subscriptions
This commit is contained in:
@@ -596,6 +596,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/search", repo.Search)
|
||||
})
|
||||
|
||||
m.Get("/repos/issues/search", repo.SearchIssues)
|
||||
|
||||
m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID)
|
||||
|
||||
m.Group("/repos", func() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@@ -22,6 +23,137 @@ import (
|
||||
milestone_service "code.gitea.io/gitea/services/milestone"
|
||||
)
|
||||
|
||||
// SearchIssues searches for issues across the repositories that the user has access to
|
||||
func SearchIssues(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/issues/search issue issueSearchIssues
|
||||
// ---
|
||||
// summary: Search for issues across the repositories that the user has access to
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: state
|
||||
// in: query
|
||||
// description: whether issue is open or closed
|
||||
// type: string
|
||||
// - name: labels
|
||||
// in: query
|
||||
// description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
|
||||
// type: string
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of requested issues
|
||||
// type: integer
|
||||
// - name: q
|
||||
// in: query
|
||||
// description: search string
|
||||
// type: string
|
||||
// - name: priority_repo_id
|
||||
// in: query
|
||||
// description: repository to prioritize in the results
|
||||
// type: integer
|
||||
// format: int64
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/IssueList"
|
||||
var isClosed util.OptionalBool
|
||||
switch ctx.Query("state") {
|
||||
case "closed":
|
||||
isClosed = util.OptionalBoolTrue
|
||||
case "all":
|
||||
isClosed = util.OptionalBoolNone
|
||||
default:
|
||||
isClosed = util.OptionalBoolFalse
|
||||
}
|
||||
|
||||
// find repos user can access (for issue search)
|
||||
repoIDs := make([]int64, 0)
|
||||
issueCount := 0
|
||||
for page := 1; ; page++ {
|
||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Page: page,
|
||||
PageSize: 15,
|
||||
Private: true,
|
||||
Keyword: "",
|
||||
OwnerID: ctx.User.ID,
|
||||
TopicOnly: false,
|
||||
Collaborate: util.OptionalBoolNone,
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.User.ID,
|
||||
OrderBy: models.SearchOrderByRecentUpdated,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(500, "SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(repos) == 0 {
|
||||
break
|
||||
}
|
||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||
for _, repo := range repos {
|
||||
switch isClosed {
|
||||
case util.OptionalBoolTrue:
|
||||
issueCount += repo.NumClosedIssues
|
||||
case util.OptionalBoolFalse:
|
||||
issueCount += repo.NumOpenIssues
|
||||
case util.OptionalBoolNone:
|
||||
issueCount += repo.NumIssues
|
||||
}
|
||||
repoIDs = append(repoIDs, repo.ID)
|
||||
}
|
||||
}
|
||||
|
||||
var issues []*models.Issue
|
||||
|
||||
keyword := strings.Trim(ctx.Query("q"), " ")
|
||||
if strings.IndexByte(keyword, 0) >= 0 {
|
||||
keyword = ""
|
||||
}
|
||||
var issueIDs []int64
|
||||
var labelIDs []int64
|
||||
var err error
|
||||
if len(keyword) > 0 && len(repoIDs) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword)
|
||||
}
|
||||
|
||||
labels := ctx.Query("labels")
|
||||
if splitted := strings.Split(labels, ","); labels != "" && len(splitted) > 0 {
|
||||
labelIDs, err = models.GetLabelIDsInReposByNames(repoIDs, splitted)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetLabelIDsInRepoByNames", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Only fetch the issues if we either don't have a keyword or the search returned issues
|
||||
// This would otherwise return all issues if no issues were found by the search.
|
||||
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
|
||||
issues, err = models.Issues(&models.IssuesOptions{
|
||||
RepoIDs: repoIDs,
|
||||
Page: ctx.QueryInt("page"),
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
IsClosed: isClosed,
|
||||
IssueIDs: issueIDs,
|
||||
LabelIDs: labelIDs,
|
||||
SortType: "priorityrepo",
|
||||
PriorityRepoID: ctx.QueryInt64("priority_repo_id"),
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ctx.Error(500, "Issues", err)
|
||||
return
|
||||
}
|
||||
|
||||
apiIssues := make([]*api.Issue, len(issues))
|
||||
for i := range issues {
|
||||
apiIssues[i] = issues[i].APIFormat()
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(issueCount, setting.UI.IssuePagingNum)
|
||||
ctx.JSON(200, &apiIssues)
|
||||
}
|
||||
|
||||
// ListIssues list the issues of a repository
|
||||
func ListIssues(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/issues issue issueListIssues
|
||||
@@ -79,7 +211,7 @@ func ListIssues(ctx *context.APIContext) {
|
||||
var labelIDs []int64
|
||||
var err error
|
||||
if len(keyword) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx.Repo.Repository.ID, keyword)
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword)
|
||||
}
|
||||
|
||||
if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 {
|
||||
|
||||
@@ -149,7 +149,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
|
||||
|
||||
var issueIDs []int64
|
||||
if len(keyword) > 0 {
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword(repo.ID, keyword)
|
||||
issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{repo.ID}, keyword)
|
||||
if err != nil {
|
||||
ctx.ServerError("issueIndexer.Search", err)
|
||||
return
|
||||
@@ -778,6 +778,9 @@ func ViewIssue(ctx *context.Context) {
|
||||
// Check if the user can use the dependencies
|
||||
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User)
|
||||
|
||||
// check if dependencies can be created across repositories
|
||||
ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies
|
||||
|
||||
// Render comments and and fetch participants.
|
||||
participants[0] = issue.Poster
|
||||
for _, comment = range issue.Comments {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// AddDependency adds new dependencies
|
||||
@@ -39,14 +40,14 @@ func AddDependency(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if both issues are in the same repo
|
||||
if issue.RepoID != dep.RepoID {
|
||||
// Check if both issues are in the same repo if cross repository dependencies is not enabled
|
||||
if issue.RepoID != dep.RepoID && !setting.Service.AllowCrossRepositoryDependencies {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_not_same_repo"))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if issue and dependency is the same
|
||||
if dep.Index == issueIndex {
|
||||
if dep.ID == issue.ID {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_same_issue"))
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user