Răsfoiți Sursa

canonical name change FE changes

Mohammed Nafees 3 ani în urmă
părinte
comite
b82fdad484

+ 17 - 1
api/server/handlers/namespace/list_releases.go

@@ -56,7 +56,23 @@ func (c *ListReleasesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	var res types.ListReleasesResponse = releases
+	var res types.ListReleasesResponse
+
+	for _, helmRel := range releases {
+		rel, err := c.Repo().Release().ReadRelease(cluster.ID, helmRel.Name, helmRel.Namespace)
+
+		if err == nil {
+			res = append(res, &types.Release{
+				Release:       helmRel,
+				PorterRelease: rel.ToReleaseType(),
+			})
+		} else {
+			res = append(res, &types.Release{
+				Release:       helmRel,
+				PorterRelease: &types.PorterRelease{},
+			})
+		}
+	}
 
 	c.WriteResult(w, r, res)
 }

+ 8 - 0
api/server/handlers/release/update_canonical_name.go

@@ -14,6 +14,7 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
 	"gorm.io/gorm"
+	"k8s.io/apimachinery/pkg/util/validation"
 )
 
 type UpdateCanonicalNameHandler struct {
@@ -56,6 +57,13 @@ func (c *UpdateCanonicalNameHandler) ServeHTTP(w http.ResponseWriter, r *http.Re
 	}
 
 	if release.CanonicalName != request.CanonicalName {
+		if request.CanonicalName != "" {
+			if errStrs := validation.IsDNS1123Label(request.CanonicalName); len(errStrs) > 0 {
+				c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(fmt.Errorf("invalid canonical name"), http.StatusBadRequest))
+				return
+			}
+		}
+
 		release.CanonicalName = request.CanonicalName
 
 		release, err = c.Repo().Release().UpdateRelease(release)

+ 1 - 2
api/types/namespace.go

@@ -4,7 +4,6 @@ import (
 	"time"
 
 	"helm.sh/helm/v3/pkg/action"
-	"helm.sh/helm/v3/pkg/release"
 	v1 "k8s.io/api/core/v1"
 )
 
@@ -83,7 +82,7 @@ type ListReleasesRequest struct {
 }
 
 // swagger:model
-type ListReleasesResponse []*release.Release
+type ListReleasesResponse []*Release
 
 type GetConfigMapRequest struct {
 	Name string `schema:"name,required"`

+ 2 - 2
api/types/release.go

@@ -40,7 +40,7 @@ type PorterRelease struct {
 	StackID string `json:"stack_id"`
 
 	// The canonical name of this release
-	CanonicalName string `json:"canonical_name,omitempty"`
+	CanonicalName string `json:"canonical_name"`
 }
 
 // swagger:model
@@ -216,5 +216,5 @@ type UpdateGitActionConfigRequest struct {
 }
 
 type UpdateCanonicalNameRequest struct {
-	CanonicalName string `json:"canonical_name" form:"required"`
+	CanonicalName string `json:"canonical_name"`
 }

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/chart/Chart.tsx

@@ -131,7 +131,7 @@ const Chart: React.FunctionComponent<Props> = ({
     >
       <Title>
         <IconWrapper>{renderIcon()}</IconWrapper>
-        {chart.name}
+        {chart.canonical_name === "" ? chart.name : chart.canonical_name}
         {chart?.config?.description && (
           <>
             <Dot style={{ marginLeft: "9px", color: "#ffffff88" }}>•</Dot>

+ 45 - 111
dashboard/src/main/home/cluster-dashboard/expanded-chart/CanonicalName.tsx

@@ -1,20 +1,15 @@
-import React, { useContext, useEffect, useMemo, useState } from "react";
+import React, { useContext, useMemo, useState } from "react";
 import styled from "styled-components";
-import { Tooltip } from "@material-ui/core";
-import Modal from "main/home/modals/Modal";
-import { TwitterPicker } from "react-color";
 import InputRow from "components/form-components/InputRow";
 import SaveButton from "components/SaveButton";
 import api from "shared/api";
 import Color from "color";
 import { Context } from "shared/Context";
 import { ChartType } from "shared/types";
-import Helper from "components/form-components/Helper";
-import { differenceBy } from "lodash";
-import SearchSelector from "components/SearchSelector";
+import { isAlphanumeric } from "shared/common";
 
 type Props = {
-  onSave: ((values: any[]) => void) | ((values: any[]) => Promise<void>);
+  onSave: (() => void) | (() => Promise<void>);
   release: ChartType;
 };
 
@@ -22,27 +17,18 @@ const CanonicalName = ({ onSave, release }: Props) => {
   const { currentProject, currentCluster, setCurrentError } = useContext(
     Context
   );
-  const [values, setValues] = useState([]);
-  const [availableTags, setAvailableTags] = useState([]);
-  const [openModal, setOpenModal] = useState(false);
   const [buttonStatus, setButtonStatus] = useState("");
-
-  const onDelete = (index: number) => {
-    setValues((prev) => {
-      const newValues = [...prev];
-      const removedTag = newValues.splice(index, 1);
-      setAvailableTags((prevAt) => [...prevAt, ...removedTag]);
-      return newValues;
-    });
-  };
+  const [canonicalName, setCanonicalName] = useState<string>(
+    release.canonical_name
+  );
 
   const handleSave = async () => {
     setButtonStatus("loading");
 
     try {
-      await api.updateReleaseTags(
+      await api.updateCanonicalName(
         "<token>",
-        { tags: [...values.map((tag) => tag.name)] },
+        { canonical_name: canonicalName },
         {
           project_id: currentProject.id,
           cluster_id: currentCluster.id,
@@ -50,14 +36,14 @@ const CanonicalName = ({ onSave, release }: Props) => {
           release_name: release.name,
         }
       );
-      await onSave(values);
+      await onSave();
       setButtonStatus("successful");
     } catch (error) {
       console.log(error);
       setCurrentError(
-        "We couldn't link the tag to the release, please try again."
+        "We couldn't change the canonical name. Please try again."
       );
-      setButtonStatus("Couldn't link the tag to the release");
+      setButtonStatus("Canonical name not changed.");
       return;
     } finally {
       setTimeout(() => {
@@ -66,60 +52,43 @@ const CanonicalName = ({ onSave, release }: Props) => {
     }
   };
 
-  const hasUnsavedChanges = useMemo(() => {
-    const hasAddedSomething = !!differenceBy(
-      values,
-      release.tags?.map((tagName: string) => ({ name: tagName })) || [],
-      "name"
-    ).length;
+  const shouldDisableSave = useMemo(() => {
+    if (canonicalName !== release.canonical_name) {
+      if (canonicalName === "") {
+        return false;
+      }
+
+      return !isAlphanumeric(canonicalName) || canonicalName.length > 63;
+    }
+
+    return true;
+  }, [canonicalName]);
 
-    const hasDeletedSomething = !!differenceBy(
-      release.tags?.map((tagName: string) => ({ name: tagName })) || [],
-      values,
-      "name"
-    ).length;
+  const saveButtonHelper = useMemo(() => {
+    if (canonicalName !== release.canonical_name) {
+      if (canonicalName !== "") {
+        if (!isAlphanumeric(canonicalName)) {
+          return "Invalid characters in the name";
+        } else if (canonicalName.length > 63) {
+          return "Name cannot exceed 63 characters";
+        }
+      }
+
+      return "Unsaved changes";
+    }
 
-    return hasAddedSomething || hasDeletedSomething;
-  }, [values, release]);
+    return "";
+  }, [canonicalName]);
 
   return (
     <>
-      <Flex>
-        {values.map((val, index) => {
-          return (
-            <Tag color={val.color} key={index}>
-              <Tooltip title={val.name}>
-                <TagText>{val.name}</TagText>
-              </Tooltip>
-              <i className="material-icons" onClick={() => onDelete(index)}>
-                cancel
-              </i>
-            </Tag>
-          );
-        })}
-      </Flex>
-      <SearchSelector
-        options={availableTags}
-        dropdownLabel="Select a tag"
-        renderAddButton={() => (
-          <AddTagButton
-            onClick={(e) => {
-              setOpenModal((prev) => !prev);
-            }}
-          >
-            + Create a new tag
-          </AddTagButton>
-        )}
-        filterBy="name"
-        onSelect={(value) => {
-          console.log(value);
-          setAvailableTags((prev) =>
-            prev.filter((prevVal) => prevVal.name !== value.name)
-          );
-          setValues((prev) => [...prev, value]);
-        }}
-        getOptionLabel={(option) => option.name}
-        renderOptionIcon={(option) => <TagColorBox color={option.color} />}
+      <InputRow
+        type="text"
+        value={canonicalName}
+        setValue={(x: string) => setCanonicalName(x)}
+        placeholder="ex: my-app"
+        isRequired={true}
+        width={"100%"}
       />
       <Flex
         style={{
@@ -127,13 +96,13 @@ const CanonicalName = ({ onSave, release }: Props) => {
         }}
       >
         <SaveButton
-          helper={hasUnsavedChanges ? "Unsaved changes" : ""}
+          helper={saveButtonHelper}
           clearPosition
+          disabled={shouldDisableSave}
           statusPosition="right"
           text="Save changes"
           onClick={() => handleSave()}
           status={buttonStatus}
-          disabled={!hasUnsavedChanges || buttonStatus === "loading"}
         ></SaveButton>
       </Flex>
       <Br />
@@ -141,18 +110,6 @@ const CanonicalName = ({ onSave, release }: Props) => {
   );
 };
 
-const AddTagButton = styled.div`
-  color: #aaaabb;
-  font-size: 13px;
-  padding: 10px 0;
-  z-index: 999;
-  padding-left: 12px;
-  cursor: pointer;
-  :hover {
-    color: white;
-  }
-`;
-
 const Br = styled.div`
   width: 100%;
   height: 10px;
@@ -193,26 +150,3 @@ const Tag = styled.div<{ color: string }>`
     }
   }
 `;
-
-const TagText = styled.span`
-  overflow-x: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-`;
-
-const Label = styled.div`
-  color: #ffffff;
-  margin-bottom: 10px;
-  display: flex;
-  align-items: center;
-  font-size: 13px;
-  font-family: "Work Sans", sans-serif;
-`;
-
-const TagColorBox = styled.div`
-  width: 15px;
-  height: 15px;
-  margin-right: 10px;
-  border-radius: 0px;
-  background-color: ${(props: { color: string }) => props.color};
-`;

+ 13 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -655,6 +655,15 @@ const ExpandedChart: React.FC<Props> = (props) => {
     );
   };
 
+  const renderHelmReleaseName = () => {
+    return (
+      <Url>
+        <Bolded>Helm Release Name:</Bolded>
+        {currentChart.name}
+      </Url>
+    );
+  };
+
   const handleUninstallChart = async () => {
     setDeleting(true);
     setCurrentOverlay(null);
@@ -874,8 +883,8 @@ const ExpandedChart: React.FC<Props> = (props) => {
                   iconWidth="33px"
                 >
                   {currentChart.canonical_name === ""
-                    ? currentChart.canonical_name
-                    : currentChart.name}
+                    ? currentChart.name
+                    : currentChart.canonical_name}
                   <DeploymentType currentChart={currentChart} />
                   <TagWrapper>
                     Namespace{" "}
@@ -886,6 +895,8 @@ const ExpandedChart: React.FC<Props> = (props) => {
                 {currentChart.chart.metadata.name != "worker" &&
                   currentChart.chart.metadata.name != "job" &&
                   renderUrl()}
+
+                {currentChart.canonical_name !== "" && renderHelmReleaseName()}
                 <InfoWrapper>
                   {/*
                   <StatusIndicator

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx

@@ -245,10 +245,10 @@ const SettingsSection: React.FC<PropsType> = ({
 
         <>
           <Heading>Canonical Name</Heading>
-          <Helper>Set a canonical name for this application</Helper>
+          <Helper>Set a canonical name for this application (lowercase letters, numbers, and "-" only)</Helper>
           <CanonicalName
             release={currentChart}
-            onSave={(val) => refreshChart()}
+            onSave={() => refreshChart()}
           />
 
           <Heading>Redeploy Webhook</Heading>

+ 4 - 3
internal/models/release.go

@@ -35,9 +35,10 @@ type Release struct {
 
 func (r *Release) ToReleaseType() *types.PorterRelease {
 	res := &types.PorterRelease{
-		ID:           r.ID,
-		WebhookToken: r.WebhookToken,
-		ImageRepoURI: r.ImageRepoURI,
+		ID:            r.ID,
+		WebhookToken:  r.WebhookToken,
+		ImageRepoURI:  r.ImageRepoURI,
+		CanonicalName: r.CanonicalName,
 	}
 
 	if r.GitActionConfig != nil {