Ivan Galakhov 4 rokov pred
rodič
commit
dad63a9106

+ 4 - 1
dashboard/src/components/SaveButton.tsx

@@ -10,6 +10,7 @@ type PropsType = {
   color?: string;
   rounded?: boolean;
   helper?: string | null;
+  saveText?: string | null;
 
   // Makes flush with corner if not within a modal
   makeFlush?: boolean;
@@ -36,7 +37,9 @@ export default class SaveButton extends Component<PropsType, StateType> {
             successful={false}
           >
             <LoadingGif src={loading} />
-            <StatusTextWrapper>Updating . . .</StatusTextWrapper>
+            <StatusTextWrapper>
+              {this.props.saveText || "Updating . . ."}
+            </StatusTextWrapper>
           </StatusWrapper>
         );
       } else if (this.props.status === "error") {

+ 76 - 38
dashboard/src/main/home/cluster-dashboard/expanded-chart/NotificationSettingsSection.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useState } from "react";
+import React, { useContext, useState, useEffect } from "react";
 import Heading from "../../../../components/form-components/Heading";
 import CheckboxRow from "../../../../components/form-components/CheckboxRow";
 import Helper from "../../../../components/form-components/Helper";
@@ -6,6 +6,7 @@ import SaveButton from "../../../../components/SaveButton";
 import api from "../../../../shared/api";
 import { Context } from "../../../../shared/Context";
 import { ChartType } from "../../../../shared/types";
+import Loading from "../../../../components/Loading";
 
 const NOTIF_CATEGORIES = ["deploy", "success", "fail"];
 
@@ -24,10 +25,37 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
       };
     }, {})
   );
+  const [initLoading, setInitLoading] = useState(true);
+  const [saveLoading, setSaveLoading] = useState(false);
+  const [numSaves, setNumSaves] = useState(0);
 
   const { currentProject, currentCluster } = useContext(Context);
 
+  useEffect(() => {
+    api
+      .getNotificationConfig(
+        "<token>",
+        {
+          namespace: props.currentChart.namespace,
+          cluster_id: currentCluster.id,
+        },
+        {
+          project_id: currentProject.id,
+          name: props.currentChart.name,
+        }
+      )
+      .then(({ data }) => {
+        setNotificationsOn(data.enabled);
+        delete data.enabled;
+        setCategories({
+          ...data,
+        });
+        setInitLoading(false);
+      });
+  }, []);
+
   const saveChanges = () => {
+    setSaveLoading(true);
     let payload = {
       enabled: notificationsOn,
       deploy: notificationsOn,
@@ -44,55 +72,65 @@ const NotificationSettingsSection: React.FC<Props> = (props) => {
         },
         {
           project_id: currentProject.id,
-          cluster_id: currentCluster.id,
           name: props.currentChart.name,
         }
       )
-      .then((data) => {
-        console.log(data);
+      .then(() => {
+        setNumSaves(numSaves + 1);
+        setSaveLoading(false);
       });
   };
 
   return (
     <>
       <Heading>Notification Settings</Heading>
-      <CheckboxRow
-        label={"Notifications Enabled"}
-        checked={notificationsOn}
-        toggle={() => setNotificationsOn(!notificationsOn)}
-        disabled={props.disabled}
-      />
-      {notificationsOn && (
+      {initLoading ? (
+        <Loading />
+      ) : (
         <>
-          <Helper>Send notifications on:</Helper>
-          {Object.entries(categories).map(([k, v]: [string, boolean]) => {
-            return (
-              <React.Fragment key={k}>
-                <CheckboxRow
-                  label={k}
-                  checked={v}
-                  toggle={() =>
-                    setCategories((prev) => {
-                      return {
-                        ...prev,
-                        [k]: !v,
-                      };
-                    })
-                  }
-                  disabled={props.disabled}
-                />
-              </React.Fragment>
-            );
-          })}
+          <CheckboxRow
+            label={"Notifications Enabled"}
+            checked={notificationsOn}
+            toggle={() => setNotificationsOn(!notificationsOn)}
+            disabled={props.disabled}
+          />
+          {notificationsOn && (
+            <>
+              <Helper>Send notifications on:</Helper>
+              {Object.entries(categories).map(([k, v]: [string, boolean]) => {
+                return (
+                  <React.Fragment key={k}>
+                    <CheckboxRow
+                      label={k}
+                      checked={v}
+                      toggle={() =>
+                        setCategories((prev) => {
+                          return {
+                            ...prev,
+                            [k]: !v,
+                          };
+                        })
+                      }
+                      disabled={props.disabled}
+                    />
+                  </React.Fragment>
+                );
+              })}
+            </>
+          )}
+          <SaveButton
+            onClick={() => saveChanges()}
+            text={"Save Changes"}
+            clearPosition={true}
+            statusPosition={"right"}
+            disabled={props.disabled || initLoading || saveLoading}
+            status={
+              saveLoading ? "loading" : numSaves > 0 ? "successful" : null
+            }
+            saveText={"Saving . . ."}
+          />
         </>
       )}
-      <SaveButton
-        onClick={() => saveChanges()}
-        text={"Save Changes"}
-        clearPosition={true}
-        statusPosition={"right"}
-        disabled={props.disabled}
-      />
     </>
   );
 };

+ 15 - 2
dashboard/src/shared/api.tsx

@@ -272,17 +272,29 @@ const updateNotificationConfig = baseApi<
   {
     payload: any;
     namespace: string;
-    cluster_id: string;
+    cluster_id: number;
   },
   {
     project_id: number;
-    cluster_id: number;
     name: string;
   }
 >("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/releases/${pathParams.name}/notifications`;
 });
 
+const getNotificationConfig = baseApi<
+  {
+    namespace: string;
+    cluster_id: number;
+  },
+  {
+    project_id: number;
+    name: string;
+  }
+>("GET", (pathParams) => {
+  return `/api/projects/${pathParams.project_id}/releases/${pathParams.name}/notifications`;
+});
+
 const deployTemplate = baseApi<
   {
     templateName: string;
@@ -1050,6 +1062,7 @@ export default {
   deleteRegistryIntegration,
   deleteSlackIntegration,
   updateNotificationConfig,
+  getNotificationConfig,
   createSubdomain,
   deployTemplate,
   deployAddon,

+ 9 - 0
internal/models/notification.go

@@ -18,3 +18,12 @@ type NotificationConfigExternal struct {
 	Success bool `json:"success"`
 	Failure bool `json:"failure"`
 }
+
+func (conf *NotificationConfig) Externalize() *NotificationConfigExternal {
+	return &NotificationConfigExternal{
+		Enabled: conf.Enabled,
+		Deploy:  conf.Deploy,
+		Success: conf.Success,
+		Failure: conf.Failure,
+	}
+}

+ 57 - 0
server/api/notifications_handler.go

@@ -5,6 +5,8 @@ import (
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/internal/models"
 	"net/http"
+	"net/url"
+	"strconv"
 )
 
 type HandleUpdateNotificationConfigForm struct {
@@ -70,3 +72,58 @@ func (app *App) HandleUpdateNotificationConfig(w http.ResponseWriter, r *http.Re
 
 	w.WriteHeader(http.StatusOK)
 }
+
+// HandleGetNotificationConfig gets the notification config for a given release
+func (app *App) HandleGetNotificationConfig(w http.ResponseWriter, r *http.Request) {
+	projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
+
+	if err != nil || projID == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	vals, err := url.ParseQuery(r.URL.RawQuery)
+	name := chi.URLParam(r, "name")
+	namespace := vals["namespace"][0]
+
+	clusterID, err := strconv.ParseUint(vals["cluster_id"][0], 10, 64)
+
+	if err != nil {
+		app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+			Code:   ErrReleaseReadData,
+			Errors: []string{"release not found"},
+		}, w)
+	}
+
+	release, err := app.Repo.Release.ReadRelease(uint(clusterID), name, namespace)
+
+	if err != nil {
+		app.sendExternalError(err, http.StatusInternalServerError, HTTPError{
+			Code:   ErrReleaseReadData,
+			Errors: []string{"release not found"},
+		}, w)
+	}
+
+	config := &models.NotificationConfigExternal{
+		Enabled: true,
+		Deploy:  true,
+		Success: true,
+		Failure: true,
+	}
+
+	if release.NotificationConfig != 0 {
+		notifConfig, err := app.Repo.NotificationConfig.ReadNotificationConfig(release.NotificationConfig)
+
+		if err != nil {
+			app.handleErrorInternal(err, w)
+		}
+
+		config = notifConfig.Externalize()
+	}
+
+	err = json.NewEncoder(w).Encode(config)
+
+	if err != nil {
+		app.handleErrorInternal(err, w)
+	}
+}

+ 10 - 0
server/router/router.go

@@ -907,6 +907,16 @@ func New(a *api.App) *chi.Mux {
 				),
 			)
 
+			r.Method(
+				"GET",
+				"/projects/{project_id}/releases/{name}/notifications",
+				auth.DoesUserHaveProjectAccess(
+					requestlog.NewHandler(a.HandleGetNotificationConfig, l),
+					mw.URLParam,
+					mw.WriteAccess,
+				),
+			)
+
 			// /api/projects/{project_id}/helmrepos routes
 			r.Method(
 				"POST",