Add API to get commits of PR (#16300)
* Add API to get commits of PR fixes #10918 Co-authored-by: Andrew Bezold <andrew.bezold@gmail.com> Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
		
							parent
							
								
									a3476e5ad5
								
							
						
					
					
						commit
						92328a3394
					
				
					 4 changed files with 215 additions and 0 deletions
				
			
		
							
								
								
									
										37
									
								
								integrations/api_pull_commits_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								integrations/api_pull_commits_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | // Copyright 2021 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package integrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | 	api "code.gitea.io/gitea/modules/structs" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestAPIPullCommits(t *testing.T) { | ||||||
|  | 	defer prepareTestEnv(t)() | ||||||
|  | 	pullIssue := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) | ||||||
|  | 	assert.NoError(t, pullIssue.LoadIssue()) | ||||||
|  | 	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: pullIssue.HeadRepoID}).(*models.Repository) | ||||||
|  | 
 | ||||||
|  | 	session := loginUser(t, "user2") | ||||||
|  | 	req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/commits", repo.OwnerName, repo.Name, pullIssue.Index) | ||||||
|  | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  | 
 | ||||||
|  | 	var commits []*api.Commit | ||||||
|  | 	DecodeJSON(t, resp, &commits) | ||||||
|  | 
 | ||||||
|  | 	if !assert.Len(t, commits, 2) { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert.Equal(t, "5f22f7d0d95d614d25a5b68592adb345a4b5c7fd", commits[0].SHA) | ||||||
|  | 	assert.Equal(t, "4a357436d925b5c974181ff12a994538ddc5a269", commits[1].SHA) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TODO add tests for already merged PR and closed PR
 | ||||||
|  | @ -905,6 +905,7 @@ func Routes() *web.Route { | ||||||
| 						m.Get(".diff", repo.DownloadPullDiff) | 						m.Get(".diff", repo.DownloadPullDiff) | ||||||
| 						m.Get(".patch", repo.DownloadPullPatch) | 						m.Get(".patch", repo.DownloadPullPatch) | ||||||
| 						m.Post("/update", reqToken(), repo.UpdatePullRequest) | 						m.Post("/update", reqToken(), repo.UpdatePullRequest) | ||||||
|  | 						m.Get("/commits", repo.GetPullRequestCommits) | ||||||
| 						m.Combo("/merge").Get(repo.IsPullRequestMerged). | 						m.Combo("/merge").Get(repo.IsPullRequestMerged). | ||||||
| 							Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest) | 							Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest) | ||||||
| 						m.Group("/reviews", func() { | 						m.Group("/reviews", func() { | ||||||
|  |  | ||||||
|  | @ -6,7 +6,9 @@ package repo | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"math" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -1101,3 +1103,122 @@ func UpdatePullRequest(ctx *context.APIContext) { | ||||||
| 
 | 
 | ||||||
| 	ctx.Status(http.StatusOK) | 	ctx.Status(http.StatusOK) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // GetPullRequestCommits gets all commits associated with a given PR
 | ||||||
|  | func GetPullRequestCommits(ctx *context.APIContext) { | ||||||
|  | 	// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/commits repository repoGetPullRequestCommits
 | ||||||
|  | 	// ---
 | ||||||
|  | 	// summary: Get commits for a pull request
 | ||||||
|  | 	// produces:
 | ||||||
|  | 	// - application/json
 | ||||||
|  | 	// parameters:
 | ||||||
|  | 	// - name: owner
 | ||||||
|  | 	//   in: path
 | ||||||
|  | 	//   description: owner of the repo
 | ||||||
|  | 	//   type: string
 | ||||||
|  | 	//   required: true
 | ||||||
|  | 	// - name: repo
 | ||||||
|  | 	//   in: path
 | ||||||
|  | 	//   description: name of the repo
 | ||||||
|  | 	//   type: string
 | ||||||
|  | 	//   required: true
 | ||||||
|  | 	// - name: index
 | ||||||
|  | 	//   in: path
 | ||||||
|  | 	//   description: index of the pull request to get
 | ||||||
|  | 	//   type: integer
 | ||||||
|  | 	//   format: int64
 | ||||||
|  | 	//   required: true
 | ||||||
|  | 	// - name: page
 | ||||||
|  | 	//   in: query
 | ||||||
|  | 	//   description: page number of results to return (1-based)
 | ||||||
|  | 	//   type: integer
 | ||||||
|  | 	// - name: limit
 | ||||||
|  | 	//   in: query
 | ||||||
|  | 	//   description: page size of results
 | ||||||
|  | 	//   type: integer
 | ||||||
|  | 	// responses:
 | ||||||
|  | 	//   "200":
 | ||||||
|  | 	//     "$ref": "#/responses/CommitList"
 | ||||||
|  | 	//   "404":
 | ||||||
|  | 	//     "$ref": "#/responses/notFound"
 | ||||||
|  | 
 | ||||||
|  | 	pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if models.IsErrPullRequestNotExist(err) { | ||||||
|  | 			ctx.NotFound() | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := pr.LoadBaseRepo(); err != nil { | ||||||
|  | 		ctx.InternalServerError(err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var prInfo *git.CompareInfo | ||||||
|  | 	baseGitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("OpenRepository", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	defer baseGitRepo.Close() | ||||||
|  | 	if pr.HasMerged { | ||||||
|  | 		prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName()) | ||||||
|  | 	} else { | ||||||
|  | 		prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) | ||||||
|  | 	} | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("GetCompareInfo", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	commits := prInfo.Commits | ||||||
|  | 
 | ||||||
|  | 	listOptions := utils.GetListOptions(ctx) | ||||||
|  | 
 | ||||||
|  | 	totalNumberOfCommits := commits.Len() | ||||||
|  | 	totalNumberOfPages := int(math.Ceil(float64(totalNumberOfCommits) / float64(listOptions.PageSize))) | ||||||
|  | 
 | ||||||
|  | 	userCache := make(map[string]*models.User) | ||||||
|  | 
 | ||||||
|  | 	start, end := listOptions.GetStartEnd() | ||||||
|  | 
 | ||||||
|  | 	if end > totalNumberOfCommits { | ||||||
|  | 		end = totalNumberOfCommits | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	apiCommits := make([]*api.Commit, end-start) | ||||||
|  | 
 | ||||||
|  | 	i := 0 | ||||||
|  | 	addedCommitsCount := 0 | ||||||
|  | 	for commitPointer := commits.Front(); commitPointer != nil; commitPointer = commitPointer.Next() { | ||||||
|  | 		if i < start { | ||||||
|  | 			i++ | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if i >= end { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		commit := commitPointer.Value.(*git.Commit) | ||||||
|  | 
 | ||||||
|  | 		// Create json struct
 | ||||||
|  | 		apiCommits[addedCommitsCount], err = convert.ToCommit(ctx.Repo.Repository, commit, userCache) | ||||||
|  | 		addedCommitsCount++ | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("toCommit", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.SetLinkHeader(int(totalNumberOfCommits), listOptions.PageSize) | ||||||
|  | 
 | ||||||
|  | 	ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) | ||||||
|  | 	ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) | ||||||
|  | 	ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumberOfCommits)) | ||||||
|  | 	ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages)) | ||||||
|  | 	ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages)) | ||||||
|  | 	ctx.JSON(http.StatusOK, &apiCommits) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -7333,6 +7333,62 @@ | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "/repos/{owner}/{repo}/pulls/{index}/commits": { | ||||||
|  |       "get": { | ||||||
|  |         "produces": [ | ||||||
|  |           "application/json" | ||||||
|  |         ], | ||||||
|  |         "tags": [ | ||||||
|  |           "repository" | ||||||
|  |         ], | ||||||
|  |         "summary": "Get commits for a pull request", | ||||||
|  |         "operationId": "repoGetPullRequestCommits", | ||||||
|  |         "parameters": [ | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "owner of the repo", | ||||||
|  |             "name": "owner", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "string", | ||||||
|  |             "description": "name of the repo", | ||||||
|  |             "name": "repo", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "integer", | ||||||
|  |             "format": "int64", | ||||||
|  |             "description": "index of the pull request to get", | ||||||
|  |             "name": "index", | ||||||
|  |             "in": "path", | ||||||
|  |             "required": true | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "integer", | ||||||
|  |             "description": "page number of results to return (1-based)", | ||||||
|  |             "name": "page", | ||||||
|  |             "in": "query" | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "type": "integer", | ||||||
|  |             "description": "page size of results", | ||||||
|  |             "name": "limit", | ||||||
|  |             "in": "query" | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         "responses": { | ||||||
|  |           "200": { | ||||||
|  |             "$ref": "#/responses/CommitList" | ||||||
|  |           }, | ||||||
|  |           "404": { | ||||||
|  |             "$ref": "#/responses/notFound" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "/repos/{owner}/{repo}/pulls/{index}/merge": { |     "/repos/{owner}/{repo}/pulls/{index}/merge": { | ||||||
|       "get": { |       "get": { | ||||||
|         "produces": [ |         "produces": [ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue