diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index ddb813cf44..861ccace81 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -27,6 +27,8 @@ type PullRequestsOptions struct { Labels []int64 MilestoneID int64 PosterID int64 + BaseBranch string + HeadBranch string } func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullRequestsOptions) *xorm.Session { @@ -51,6 +53,14 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR sess.And("issue.poster_id=?", opts.PosterID) } + if opts.BaseBranch != "" { + sess.And("pull_request.base_branch=?", opts.BaseBranch) + } + + if opts.HeadBranch != "" { + sess.And("pull_request.head_branch=?", opts.HeadBranch) + } + return sess } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 1f82bfce1b..37be508512 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -90,6 +90,14 @@ func ListPullRequests(ctx *context.APIContext) { // in: query // description: Filter by pull request author // type: string + // - name: base + // in: query + // description: Filter by base branch name + // type: string + // - name: head + // in: query + // description: Filter by head branch name + // type: string // - name: page // in: query // description: Page number of results to return (1-based) @@ -137,6 +145,8 @@ func ListPullRequests(ctx *context.APIContext) { Labels: labelIDs, MilestoneID: ctx.FormInt64("milestone"), PosterID: posterID, + BaseBranch: ctx.FormTrim("base"), + HeadBranch: ctx.FormTrim("head"), }) if err != nil { ctx.Error(http.StatusInternalServerError, "PullRequests", err) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 8ca012823a..e872051b18 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14003,6 +14003,18 @@ "name": "poster", "in": "query" }, + { + "type": "string", + "description": "Filter by base branch name", + "name": "base", + "in": "query" + }, + { + "type": "string", + "description": "Filter by head branch name", + "name": "head", + "in": "query" + }, { "minimum": 1, "type": "integer", diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index c1b64cdb39..17fab07f9e 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -178,6 +178,59 @@ func TestAPIPullsFiles(t *testing.T) { }) } +func TestAPIViewPullsFilterByBaseHead(t *testing.T) { + defer tests.PrepareTestEnv(t)() + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeReadRepository) + + t.Run("FilterByBase", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&base=master", repo.OwnerName, repo.Name). + AddTokenAuth(ctx.Token) + resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + + var pulls []*api.PullRequest + DecodeJSON(t, resp, &pulls) + assert.Len(t, pulls, 2) + for _, pr := range pulls { + assert.Equal(t, "master", pr.Base.Name) + } + }) + + t.Run("FilterByHead", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&head=branch2", repo.OwnerName, repo.Name). + AddTokenAuth(ctx.Token) + resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + + var pulls []*api.PullRequest + DecodeJSON(t, resp, &pulls) + assert.Len(t, pulls, 1) + assert.Equal(t, "branch2", pulls[0].Head.Name) + }) + + t.Run("FilterByBaseAndHead", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&base=master&head=branch2", repo.OwnerName, repo.Name). + AddTokenAuth(ctx.Token) + resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + + var pulls []*api.PullRequest + DecodeJSON(t, resp, &pulls) + assert.Len(t, pulls, 1) + assert.Equal(t, "master", pulls[0].Base.Name) + assert.Equal(t, "branch2", pulls[0].Head.Name) + }) + + t.Run("FilterByBaseNoMatch", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&base=nonexistent", repo.OwnerName, repo.Name). + AddTokenAuth(ctx.Token) + resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + + var pulls []*api.PullRequest + DecodeJSON(t, resp, &pulls) + assert.Empty(t, pulls) + }) +} + func TestAPIViewPullsByBaseHead(t *testing.T) { defer tests.PrepareTestEnv(t)() repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})