瀏覽代碼

chore: add branch metadata endpoint and modify frontend

Soham Parekh 3 年之前
父節點
當前提交
afb93e44e7

+ 71 - 0
api/server/handlers/gitinstallation/get_branch_metadata.go

@@ -0,0 +1,71 @@
+package gitinstallation
+
+import (
+	"context"
+	"net/http"
+
+	"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/commonutils"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+)
+
+type GithubGetBranchMetadataHandler struct {
+	handlers.PorterHandlerWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGithubGetBranchMetadataHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *GithubGetBranchMetadataHandler {
+	return &GithubGetBranchMetadataHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *GithubGetBranchMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	owner, name, ok := commonutils.GetOwnerAndNameParams(c, w, r)
+
+	if !ok {
+		return
+	}
+
+	branch, ok := commonutils.GetBranchParam(c, w, r)
+
+	if !ok {
+		return
+	}
+
+	client, err := GetGithubAppClientFromRequest(c.Config(), r)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	branchResp, _, err := client.Repositories.GetBranch(
+		context.Background(),
+		owner,
+		name,
+		branch,
+		false,
+	)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	branchMeta := &types.GetGithubBranchMetadataResponse{
+		Name:          branchResp.GetName(),
+		Commit:        branchResp.GetCommit().GetSHA(),
+		LastUpdatedAt: branchResp.GetCommit().GetCommit().GetAuthor().GetDate(),
+		URL:           branchResp.GetCommit().GetHTMLURL(),
+	}
+
+	c.WriteResult(w, r, branchMeta)
+}

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

@@ -514,6 +514,42 @@ func getGitInstallationRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{ownner}/{name}/branches/{branch} ->
+	// gitinstallation.GithubGetBranchMetadataHandler
+	getBranchMetadataEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbList,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent: basePath,
+				RelativePath: fmt.Sprintf(
+					"%s/repos/{%s}/{%s}/{%s}/branches/{%s}",
+					relPath,
+					types.URLParamGitKind,
+					types.URLParamGitRepoOwner,
+					types.URLParamGitRepoName,
+					types.URLParamGitBranch,
+				),
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.GitInstallationScope,
+			},
+		},
+	)
+
+	getBranchMetadataHandler := gitinstallation.NewGithubGetBranchMetadataHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &router.Route{
+		Endpoint: getBranchMetadataEndpoint,
+		Handler:  getBranchMetadataHandler,
+		Router:   r,
+	})
+
 	//  GET /api/projects/{project_id}/gitrepos/{installation_id}/repos/{kind}/{owner}/{name}/{branch}/buildpack/detect ->
 	// gitinstallation.NewGithubGetBuildpackHandler
 	getBuildpackEndpoint := factory.NewAPIEndpoint(

+ 9 - 0
api/types/git_installation.go

@@ -1,5 +1,7 @@
 package types
 
+import "time"
+
 type GitInstallation struct {
 	ID uint `json:"id"`
 
@@ -32,6 +34,13 @@ const (
 
 type ListRepoBranchesResponse []string
 
+type GetGithubBranchMetadataResponse struct {
+	Name          string    `json:"name"`
+	Commit        string    `json:"commit_sha"`
+	LastUpdatedAt time.Time `json:"last_updated_at"`
+	URL           string    `json:"url"`
+}
+
 type GithubDirectoryRequest struct {
 	Dir string `schema:"dir" form:"required"`
 }

+ 53 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/BranchRow.tsx

@@ -0,0 +1,53 @@
+import React from "react";
+import styled from "styled-components";
+import { EllipsisTextWrapper } from "../components/styled";
+import pr_icon from "assets/pull_request_icon.svg";
+
+interface Props {
+  branch: string;
+  onClick: () => void;
+  isLast?: boolean;
+  isSelected?: boolean;
+}
+
+const BranchRow = ({ branch, onClick, isLast, isSelected }: Props) => {
+  return (
+    <Row onClick={onClick} isLast={isLast} isSelected={isSelected}>
+      <BranchName>
+        <BranchIcon src={pr_icon} alt="branch icon" />
+        <EllipsisTextWrapper tooltipText={branch}>{branch}</EllipsisTextWrapper>
+      </BranchName>
+    </Row>
+  );
+};
+
+export default BranchRow;
+
+const Row = styled.div<{ isLast?: boolean; isSelected?: boolean }>`
+  width: 100%;
+  padding: 15px;
+  cursor: pointer;
+  background: ${(props) => (props.isSelected ? "#ffffff11" : "#26292e")};
+  border-bottom: ${(props) => (props.isLast ? "" : "1px solid #494b4f")};
+  :hover {
+    background: #ffffff11;
+  }
+`;
+
+const BranchName = styled.div`
+  font-family: "Work Sans", sans-serif;
+  font-weight: 500;
+  color: #ffffff;
+  display: flex;
+  font-size: 14px;
+  align-items: center;
+  margin-bottom: 10px;
+`;
+
+const BranchIcon = styled.img`
+  font-size: 20px;
+  height: 16px;
+  margin-right: 10px;
+  color: #aaaabb;
+  opacity: 50%;
+`;

+ 24 - 68
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/CreateBranchEnvironment.tsx

@@ -17,6 +17,7 @@ import pr_icon from "assets/pull_request_icon.svg";
 import { search } from "shared/search";
 import RadioFilter from "components/RadioFilter";
 import sort from "assets/sort.svg";
+import BranchRow from "./BranchRow";
 
 interface Props {
   environmentID: string;
@@ -29,11 +30,9 @@ const CreateBranchEnvironment = ({ environmentID }: Props) => {
   const [sortOrder, setSortOrder] = useState("Newest");
   const [loading, setLoading] = useState<boolean>(false);
   const [showErrorsModal, setShowErrorsModal] = useState<boolean>(false);
-  const {
-    currentProject,
-    currentCluster,
-    setCurrentError,
-  } = useContext(Context);
+  const { currentProject, currentCluster, setCurrentError } = useContext(
+    Context
+  );
 
   const {
     data: environment,
@@ -110,13 +109,9 @@ const CreateBranchEnvironment = ({ environmentID }: Props) => {
   };
 
   const filteredBranches = useMemo(() => {
-    const filteredBySearch = search<string>(
-      branches ?? [],
-      searchValue,
-      {
-        isCaseSensitive: false,
-      }
-    );
+    const filteredBySearch = search<string>(branches ?? [], searchValue, {
+      isCaseSensitive: false,
+    });
 
     switch (sortOrder) {
       case "Alphabetical":
@@ -132,12 +127,10 @@ const CreateBranchEnvironment = ({ environmentID }: Props) => {
         {
           disable_new_comments: environment.new_comments_disabled,
           ...environment,
-          git_deploy_branches: _.uniq(
-            [
-              ...environmentGitDeployBranches,
-              selectedBranch,
-            ]
-          ),
+          git_deploy_branches: _.uniq([
+            ...environmentGitDeployBranches,
+            selectedBranch,
+          ]),
         },
         {
           project_id: currentProject.id,
@@ -206,31 +199,21 @@ const CreateBranchEnvironment = ({ environmentID }: Props) => {
             icon={sort}
             selected={sortOrder}
             setSelected={setSortOrder}
-            options={[
-              { label: "Alphabetical", value: "Alphabetical" },
-            ]}
+            options={[{ label: "Alphabetical", value: "Alphabetical" }]}
             name="Sort"
           />
         </Flex>
       </FlexRow>
       <Br height="10px" />
       <BranchList>
-      {
-        (filteredBranches ?? []).map((branch, i) => (
+        {(filteredBranches ?? []).map((branch, i) => (
           <BranchRow
+            branch={branch}
             onClick={() => handleRowItemClick(branch)}
             isLast={i === filteredBranches.length - 1}
             isSelected={branch === selectedBranch}
-          >
-            <BranchName>
-              <BranchIcon src={pr_icon} alt="branch icon" />
-                <EllipsisTextWrapper tooltipText={branch}>
-                  {branch}
-                </EllipsisTextWrapper>
-            </BranchName>
-          </BranchRow>
-        ))
-      }
+          />
+        ))}
       </BranchList>
       {showErrorsModal && selectedBranch ? (
         <PorterYAMLErrorsModal
@@ -255,13 +238,15 @@ const CreateBranchEnvironment = ({ environmentID }: Props) => {
         <SubmitButton
           onClick={() => updateDeployBranchesMutation.mutate()}
           disabled={
-            updateDeployBranchesMutation.isLoading || loading
-            || porterYAMLErrors.length > 0 || !selectedBranch
+            updateDeployBranchesMutation.isLoading ||
+            loading ||
+            porterYAMLErrors.length > 0 ||
+            !selectedBranch
           }
         >
-          {
-            updateDeployBranchesMutation.isLoading ? 'Creating...' : 'Create Preview Deployment'
-          }
+          {updateDeployBranchesMutation.isLoading
+            ? "Creating..."
+            : "Create Preview Deployment"}
         </SubmitButton>
         {selectedBranch && porterYAMLErrors.length ? (
           <RevalidatePorterYAMLSpanWrapper>
@@ -296,17 +281,6 @@ const BranchList = styled.div`
   margin-top: 33px;
 `;
 
-const BranchRow = styled.div<{ isLast?: boolean; isSelected?: boolean }>`
-  width: 100%;
-  padding: 15px;
-  cursor: pointer;
-  background: ${(props) => (props.isSelected ? "#ffffff11" : "#26292e")};
-  border-bottom: ${(props) => (props.isLast ? "" : "1px solid #494b4f")};
-  :hover {
-    background: #ffffff11;
-  }
-`;
-
 const SearchRowWrapper = styled.div`
   display: flex;
   align-items: center;
@@ -332,16 +306,6 @@ const SearchBarWrapper = styled.div`
   }
 `;
 
-const BranchName = styled.div`
-  font-family: "Work Sans", sans-serif;
-  font-weight: 500;
-  color: #ffffff;
-  display: flex;
-  font-size: 14px;
-  align-items: center;
-  margin-bottom: 10px;
-`;
-
 const Code = styled.span`
   font-family: monospace; ;
 `;
@@ -405,14 +369,6 @@ const MergeInfo = styled.div`
   }
 `;
 
-const BranchIcon = styled.img`
-  font-size: 20px;
-  height: 16px;
-  margin-right: 10px;
-  color: #aaaabb;
-  opacity: 50%;
-`;
-
 const SubmitButton = styled.div`
   display: flex;
   flex-direction: row;
@@ -518,4 +474,4 @@ const RefreshButton = styled.button`
     background-color: rgb(97 98 102 / 44%);
     color: white;
   }
-`;
+`;