Ver código fonte

add list branches endpoint

Alexander Belanger 4 anos atrás
pai
commit
d9224ecdfe

+ 9 - 8
api/server/authz/policy.go

@@ -8,6 +8,7 @@ import (
 	"github.com/porter-dev/porter/api/server/authz/policy"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 )
@@ -94,21 +95,21 @@ func getRequestActionForEndpoint(
 
 		switch scope {
 		case types.ProjectScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamProjectID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamProjectID)
 		case types.ClusterScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamClusterID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamClusterID)
 		case types.RegistryScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamRegistryID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamRegistryID)
 		case types.HelmRepoScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamHelmRepoID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamHelmRepoID)
 		case types.GitInstallationScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamGitInstallationID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamGitInstallationID)
 		case types.InfraScope:
-			resource.UInt, reqErr = GetURLParamUint(r, string(types.URLParamInfraID))
+			resource.UInt, reqErr = requestutils.GetURLParamUint(r, types.URLParamInfraID)
 		case types.NamespaceScope:
-			resource.Name, reqErr = GetURLParamString(r, string(types.URLParamNamespace))
+			resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamNamespace)
 		case types.ReleaseScope:
-			resource.Name, reqErr = GetURLParamString(r, string(types.URLParamReleaseName))
+			resource.Name, reqErr = requestutils.GetURLParamString(r, types.URLParamReleaseName)
 		}
 
 		if reqErr != nil {

+ 2 - 1
api/server/authz/release.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"helm.sh/helm/v3/pkg/release"
@@ -46,7 +47,7 @@ func (p *ReleaseScopedMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Reque
 	name := reqScopes[types.ReleaseScope].Resource.Name
 
 	// get the version for the application
-	version, _ := GetURLParamUint(r, string(types.URLParamReleaseVersion))
+	version, _ := requestutils.GetURLParamUint(r, types.URLParamReleaseVersion)
 
 	release, err := helmAgent.GetRelease(name, int(version))
 

+ 122 - 0
api/server/handlers/gitinstallation/list_branches.go

@@ -0,0 +1,122 @@
+package gitinstallation
+
+import (
+	"context"
+	"net/http"
+	"sync"
+
+	"github.com/google/go-github/github"
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type GithubListBranchesHandler struct {
+	handlers.PorterHandlerWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGithubListBranchesHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *GithubListBranchesHandler {
+	return &GithubListBranchesHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *GithubListBranchesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	owner, reqErr := requestutils.GetURLParamString(r, types.URLParamGitRepoOwner)
+
+	if reqErr != nil {
+		return
+	}
+
+	name, reqErr := requestutils.GetURLParamString(r, types.URLParamGitRepoName)
+
+	if reqErr != nil {
+		return
+	}
+
+	client, err := GetGithubAppClientFromRequest(c.Config(), r)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// List all branches for a specified repo
+	allBranches, resp, err := client.Repositories.ListBranches(context.Background(), owner, name, &github.ListOptions{
+		PerPage: 100,
+	})
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	// make workers to get branches concurrently
+	const WCOUNT = 5
+	numPages := resp.LastPage + 1
+	var workerErr error
+	var mu sync.Mutex
+	var wg sync.WaitGroup
+
+	worker := func(cp int) {
+		defer wg.Done()
+
+		for cp < numPages {
+			opts := &github.ListOptions{
+				Page:    cp,
+				PerPage: 100,
+			}
+
+			branches, _, err := client.Repositories.ListBranches(context.Background(), owner, name, opts)
+
+			if err != nil {
+				mu.Lock()
+				workerErr = err
+				mu.Unlock()
+				return
+			}
+
+			mu.Lock()
+			allBranches = append(allBranches, branches...)
+			mu.Unlock()
+
+			cp += WCOUNT
+		}
+	}
+
+	var numJobs int
+	if numPages > WCOUNT {
+		numJobs = WCOUNT
+	} else {
+		numJobs = numPages
+	}
+
+	wg.Add(numJobs)
+
+	// page 1 is already loaded so we start with 2
+	for i := 1; i <= numJobs; i++ {
+		go worker(i + 1)
+	}
+
+	wg.Wait()
+
+	if workerErr != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res := make(types.ListRepoBranchesResponse, 0)
+	for _, b := range allBranches {
+		res = append(res, b.GetName())
+	}
+
+	c.WriteResult(w, r, res)
+}

+ 37 - 0
api/server/router/git_installation.go

@@ -1,6 +1,8 @@
 package router
 
 import (
+	"fmt"
+
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/api/server/handlers/gitinstallation"
 	"github.com/porter-dev/porter/api/server/shared"
@@ -109,5 +111,40 @@ func getGitInstallationRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/branches ->
+	// gitinstallation.GithubListBranchesHandler
+	listBranchesEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbList,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent: basePath,
+				RelativePath: fmt.Sprintf(
+					"%s/repos/%s/%s/%s/branches",
+					relPath,
+					types.URLParamGitKind,
+					types.URLParamGitRepoOwner,
+					types.URLParamGitRepoName,
+				),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.GitInstallationScope,
+			},
+		},
+	)
+
+	listBranchesHandler := gitinstallation.NewGithubListBranchesHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: listBranchesEndpoint,
+		Handler:  listBranchesHandler,
+		Router:   r,
+	})
+
 	return routes, newPath
 }

+ 5 - 4
api/server/authz/param.go → api/server/shared/requestutils/url_param.go

@@ -1,4 +1,4 @@
-package authz
+package requestutils
 
 import (
 	"fmt"
@@ -7,6 +7,7 @@ import (
 
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/types"
 )
 
 const urlParamNotFoundFmt = "could not find url param %s"
@@ -14,8 +15,8 @@ const urlParamErrUintConvFmt = "could not convert url parameter %s to uint, got
 
 // GetURLParamString returns a specific URL parameter as a string using
 // chi.URLParam. It returns an internal server error if the URL parameter is not found.
-func GetURLParamString(r *http.Request, param string) (string, apierrors.RequestError) {
-	urlParam := chi.URLParam(r, param)
+func GetURLParamString(r *http.Request, param types.URLParam) (string, apierrors.RequestError) {
+	urlParam := chi.URLParam(r, string(param))
 
 	if urlParam == "" {
 		// this is an internal server error, since it means the handler requested an
@@ -28,7 +29,7 @@ func GetURLParamString(r *http.Request, param string) (string, apierrors.Request
 
 // GetURLParamUint returns a URL parameter as a uint. It returns
 // an internal server error if the URL parameter is not found.
-func GetURLParamUint(r *http.Request, param string) (uint, apierrors.RequestError) {
+func GetURLParamUint(r *http.Request, param types.URLParam) (uint, apierrors.RequestError) {
 	urlParam, reqErr := GetURLParamString(r, param)
 
 	if reqErr != nil {

+ 6 - 5
api/server/authz/param_test.go → api/server/shared/requestutils/url_param_test.go

@@ -1,4 +1,4 @@
-package authz_test
+package requestutils_test
 
 import (
 	"context"
@@ -7,9 +7,10 @@ import (
 	"testing"
 
 	"github.com/go-chi/chi"
-	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/apitest"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -58,7 +59,7 @@ func TestGetURLUintParamsErrors(t *testing.T) {
 		// set the context for testing
 		r = apitest.WithURLParams(t, r, test.routeParams)
 
-		_, err := authz.GetURLParamUint(r, test.paramReq)
+		_, err := requestutils.GetURLParamUint(r, types.URLParam(test.paramReq))
 
 		if err == nil {
 			t.Fatalf("[ %s ] did not return an error when error was expected", test.description)
@@ -89,7 +90,7 @@ func TestGetURLParamString(t *testing.T) {
 
 	r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rctx))
 
-	res, err := authz.GetURLParamString(r, "name")
+	res, err := requestutils.GetURLParamString(r, "name")
 
 	if err != nil {
 		t.Fatalf("[ GetURLParamString ] returneed an error when no error was expected, %v", err.Error())
@@ -111,7 +112,7 @@ func TestGetURLParamUint(t *testing.T) {
 
 	r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rctx))
 
-	res, err := authz.GetURLParamUint(r, "name")
+	res, err := requestutils.GetURLParamUint(r, "name")
 
 	if err != nil {
 		t.Fatalf("[ GetURLParamUint ] returneed an error when no error was expected, %v", err.Error())

+ 8 - 0
api/types/git_installation.go

@@ -21,3 +21,11 @@ type Repo struct {
 }
 
 type ListReposResponse []Repo
+
+const (
+	URLParamGitKind      URLParam = "kind"
+	URLParamGitRepoOwner URLParam = "owner"
+	URLParamGitRepoName  URLParam = "name"
+)
+
+type ListRepoBranchesResponse []string

+ 1 - 1
docs/developing/backend-refactor-status.md

@@ -52,7 +52,7 @@
 | <li>- [ ] `POST /api/projects/{project_id}/deploy/{name}/{version}`                                                         |             |                 |             |                  |
 | <li>- [X] `GET /api/projects/{project_id}/gitrepos`                                                                         | AB          |                 |             |                  |
 | <li>- [X] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos`                                                 | AB          |                 |             |                  |
-| <li>- [ ] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/branches`                  |             |                 |             |                  |
+| <li>- [X] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/branches`                  | AB          |                 |             |                  |
 | <li>- [ ] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/buildpack/detect` |             |                 |             |                  |
 | <li>- [ ] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/contents`         |             |                 |             |                  |
 | <li>- [ ] `GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/procfile`         |             |                 |             |                  |