2
0
Эх сурвалжийг харах

add notification type config (#4271)

d-g-town 2 жил өмнө
parent
commit
9a529477e5

+ 12 - 0
api/server/handlers/notifications/get_notification_config.go

@@ -101,6 +101,11 @@ func configFromProto(proto *porterv1.NotificationConfig) Config {
 		statuses = append(statuses, Status{transformProtoToStatusString[status]})
 	}
 
+	var types []Type
+	for _, t := range proto.EventTypes {
+		types = append(types, Type{transformProtoToTypeString[t]})
+	}
+
 	var mention string
 	if proto.SlackConfig != nil && len(proto.SlackConfig.Mentions) > 0 {
 		mention = proto.SlackConfig.Mentions[0]
@@ -109,6 +114,7 @@ func configFromProto(proto *porterv1.NotificationConfig) Config {
 	return Config{
 		Statuses: statuses,
 		Mention:  mention,
+		Types:    types,
 	}
 }
 
@@ -117,3 +123,9 @@ var transformProtoToStatusString = map[porterv1.EnumNotificationStatus]string{
 	porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_FAILED:      "failed",
 	porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_PROGRESSING: "progressing",
 }
+
+var transformProtoToTypeString = map[porterv1.EnumNotificationEventType]string{
+	porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_DEPLOY:    "deploy",
+	porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_PREDEPLOY: "pre-deploy",
+	porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_BUILD:     "build",
+}

+ 18 - 0
api/server/handlers/notifications/update_notification_config.go

@@ -44,6 +44,7 @@ type UpdateNotificationConfigRequest struct {
 type Config struct {
 	Mention  string   `json:"mention"`
 	Statuses []Status `json:"statuses"`
+	Types    []Type   `json:"types"`
 }
 
 // Status is a wrapper object over a string for zod validation
@@ -51,6 +52,11 @@ type Status struct {
 	Status string `json:"status"`
 }
 
+// Type is a wrapper object over a string for zod validation
+type Type struct {
+	Type string `json:"type"`
+}
+
 // UpdateNotificationConfigResponse is the response object for the /notifications/{notification_config_id} endpoint
 type UpdateNotificationConfigResponse struct {
 	ID uint `json:"id"`
@@ -117,8 +123,14 @@ func configToProto(config Config) *porterv1.NotificationConfig {
 		statuses = append(statuses, transformStatusStringToProto[status.Status])
 	}
 
+	var types []porterv1.EnumNotificationEventType
+	for _, t := range config.Types {
+		types = append(types, transformTypeStringToProto[t.Type])
+	}
+
 	return &porterv1.NotificationConfig{
 		Statuses:    statuses,
+		EventTypes:  types,
 		SlackConfig: &porterv1.SlackConfig{Mentions: []string{config.Mention}},
 	}
 }
@@ -128,3 +140,9 @@ var transformStatusStringToProto = map[string]porterv1.EnumNotificationStatus{
 	"failed":      porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_FAILED,
 	"progressing": porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_PROGRESSING,
 }
+
+var transformTypeStringToProto = map[string]porterv1.EnumNotificationEventType{
+	"deploy":     porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_DEPLOY,
+	"build":      porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_BUILD,
+	"pre-deploy": porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_PREDEPLOY,
+}

+ 7 - 9
dashboard/src/components/porter/Icon.tsx

@@ -4,16 +4,13 @@ import styled from "styled-components";
 type Props = {
   src: any;
   height?: string;
+  width?: string;
   opacity?: number;
 };
 
-const Icon: React.FC<Props> = ({
-  src,
-  height,
-  opacity,
-}) => {
+const Icon: React.FC<Props> = ({ src, height, width, opacity }) => {
   return (
-    <StyledIcon src={src} height={height} opacity={opacity} />
+    <StyledIcon src={src} height={height} opacity={opacity} width={width} />
   );
 };
 
@@ -23,6 +20,7 @@ const StyledIcon = styled.img<{
   height?: string;
   opacity?: number;
 }>`
-  height: ${props => props.height || "20px"};
-  opacity: ${props => props.opacity || 1};
-`;
+  ${(props) =>
+    props.width ? "width: 20px;" : `height: ${props.height || "20px"};`}
+  opacity: ${(props) => props.opacity || 1};
+`;

+ 11 - 1
dashboard/src/lib/notifications/types.ts

@@ -8,7 +8,16 @@ export const notificationConfigFormValidator = z.object({
     .object({
       status: z.string(),
     })
-    .array(),
+    .array()
+    .nullish()
+    .default([]),
+  types: z
+    .object({
+      type: z.string(),
+    })
+    .array()
+    .nullish()
+    .default([]),
 });
 export type NotificationConfigFormData = z.infer<
   typeof notificationConfigFormValidator
@@ -17,4 +26,5 @@ export type NotificationConfigFormData = z.infer<
 export const emptyNotificationConfig: NotificationConfigFormData = {
   mention: "",
   statuses: [],
+  types: [],
 };

+ 73 - 9
dashboard/src/main/home/integrations/SlackIntegrationList.tsx

@@ -28,7 +28,10 @@ import {
 
 import api from "shared/api";
 import { Context } from "shared/Context";
+import build from "assets/build.png";
+import deploy from "assets/deploy.png";
 import hash from "assets/hash-02.svg";
+import pre_deploy from "assets/pre_deploy.png";
 import save from "assets/save-01.svg";
 
 type SlackIntegrationListProps = {
@@ -41,13 +44,19 @@ const statusOptions = [
   { value: "progressing", emoji: "🚀", label: "Progressing" },
 ];
 
+const typeOptions = [
+  { value: "deployment", icon: deploy, label: "Deploy" },
+  { value: "pre-deploy", icon: pre_deploy, label: "Pre-deploy" },
+  { value: "build", icon: build, label: "Build" },
+];
+
 const SlackIntegrationList: React.FC<SlackIntegrationListProps> = (props) => {
   const [isDelete, setIsDelete] = useState(false);
   const [deleteIndex, setDeleteIndex] = useState(-1); // guaranteed to be set when used
   const { currentProject, setCurrentError } = useContext(Context);
   const deleted = useRef(new Set());
 
-  const handleDelete = () => {
+  const handleDelete = (): void => {
     api
       .deleteSlackIntegration(
         "<token>",
@@ -195,18 +204,43 @@ const NotificationConfigContainer: React.FC<
     register,
   } = notificationForm;
 
-  const { append, remove, fields } = useFieldArray({
+  const {
+    append: statusAppend,
+    remove: statusRemove,
+    fields: statusFields,
+  } = useFieldArray({
     control,
     name: "statuses",
   });
 
-  const onAdd = (
+  const onAddStatuses = (
     inp: IterableElement<NotificationConfigFormData["statuses"]>
   ): void => {
-    const previouslyAdded = fields.findIndex((s) => s.status === inp.status);
+    const previouslyAdded = statusFields.findIndex(
+      (s) => s.status === inp.status
+    );
 
     if (previouslyAdded === -1) {
-      append(inp);
+      statusAppend(inp);
+    }
+  };
+
+  const {
+    append: typeAppend,
+    remove: typeRemove,
+    fields: typeFields,
+  } = useFieldArray({
+    control,
+    name: "types",
+  });
+
+  const onAddTypes = (
+    inp: IterableElement<NotificationConfigFormData["types"]>
+  ): void => {
+    const previouslyAdded = typeFields.findIndex((s) => s.type === inp.type);
+
+    if (previouslyAdded === -1) {
+      typeAppend(inp);
     }
   };
 
@@ -258,12 +292,42 @@ const NotificationConfigContainer: React.FC<
 
   return (
     <>
-      <Text>Filter deployment notifications:</Text>
+      <Text>Filter notification types:</Text>
+      <Spacer y={0.5} />
+      <SelectableList
+        scroll={false}
+        listItems={typeOptions.map((option) => {
+          const selectedOptionsIdx = typeFields.findIndex(
+            (s) => s.type === option.value
+          );
+          return {
+            selectable: (
+              <Container row>
+                <Spacer inline width="1px" />
+                <Icon src={option.icon} width={"8px"} />
+                <Spacer inline width="10px" />
+                <Text size={12}>{option.label}</Text>
+                <Spacer inline x={1} />
+              </Container>
+            ),
+            key: option.value,
+            onSelect: () => {
+              onAddTypes({ type: option.value });
+            },
+            onDeselect: () => {
+              typeRemove(selectedOptionsIdx);
+            },
+            isSelected: selectedOptionsIdx !== -1,
+          };
+        })}
+      />
+      <Spacer y={0.75} />
+      <Text>Filter notification statuses:</Text>
       <Spacer y={0.5} />
       <SelectableList
         scroll={false}
         listItems={statusOptions.map((option) => {
-          const selectedOptionsIdx = fields.findIndex(
+          const selectedOptionsIdx = statusFields.findIndex(
             (s) => s.status === option.value
           );
           return {
@@ -280,10 +344,10 @@ const NotificationConfigContainer: React.FC<
             ),
             key: option.value,
             onSelect: () => {
-              onAdd({ status: option.value });
+              onAddStatuses({ status: option.value });
             },
             onDeselect: () => {
-              remove(selectedOptionsIdx);
+              statusRemove(selectedOptionsIdx);
             },
             isSelected: selectedOptionsIdx !== -1,
           };