Feroze Mohideen 2 lat temu
rodzic
commit
386e97a763

+ 0 - 0
dashboard/src/lib/porter-apps/notification.ts


+ 8 - 1
dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx

@@ -48,6 +48,7 @@ import HelmEditorTab from "./tabs/HelmEditorTab";
 import HelmLatestValuesTab from "./tabs/HelmLatestValuesTab";
 import { Context } from "shared/Context";
 import { useIntercom } from "lib/hooks/useIntercom";
+import Notifications from "./tabs/Notifications";
 
 // commented out tabs are not yet implemented
 // will be included as support is available based on data from app revisions rather than helm releases
@@ -65,6 +66,7 @@ const validTabs = [
   "helm-overrides",
   "helm-values",
   "job-history",
+  "notifications",
 ] as const;
 const DEFAULT_TAB = "activity";
 type ValidTab = typeof validTabs[number];
@@ -97,6 +99,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
     servicesFromYaml,
     appEnv,
     setPreviewRevision,
+    latestNotifications,
   } = useLatestRevision();
   const { validateApp } = useAppValidation({
     deploymentTargetID: deploymentTarget.id,
@@ -400,7 +403,10 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
   }, [isSubmitting, JSON.stringify(errors)]);
 
   const tabs = useMemo(() => {
+    const numNotifications = latestNotifications.length;
+
     const base = [
+      { label: `Notifications (${numNotifications})`, value: "notifications"},
       { label: "Activity", value: "activity" },
       { label: "Overview", value: "overview" },
       { label: "Logs", value: "logs" },
@@ -434,7 +440,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
     }
     base.push({ label: "Settings", value: "settings" });
     return base;
-  }, [deploymentTarget.preview, latestProto.build]);
+  }, [deploymentTarget.preview, latestProto.build, latestNotifications.length]);
 
   useEffect(() => {
     const newProto = previewRevision
@@ -562,6 +568,7 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
             />
           ))
           .with("helm-values", () => <HelmLatestValuesTab />)
+          .with("notifications", () => <Notifications />)
           .otherwise(() => null)}
         <Spacer y={2} />
       </form>

+ 4 - 10
dashboard/src/main/home/app-dashboard/app-view/tabs/Notifications.tsx

@@ -1,22 +1,16 @@
 import React from "react";
 import { useLatestRevision } from "../LatestRevisionContext";
-import ActivityFeed from "./activity-feed/ActivityFeed";
+import NotificationFeed from "./notifications/NotificationFeed";
 
 const Notifications: React.FC = () => {
   const {
-    projectId,
-    clusterId,
-    latestProto,
-    deploymentTarget,
+    latestNotifications,
   } = useLatestRevision();
 
   return (
     <>
-      <ActivityFeed
-        currentProject={projectId}
-        currentCluster={clusterId}
-        appName={latestProto.name}
-        deploymentTargetId={deploymentTarget.id}
+      <NotificationFeed
+        notifications={latestNotifications}
       />
     </>
   );

+ 1 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/ActivityFeed.tsx

@@ -45,7 +45,7 @@ const ActivityFeed: React.FC<Props> = ({ appName, deploymentTargetId, currentClu
                 || event.metadata?.short_summary?.includes("requesting more CPU than is available")
                 || event.metadata?.short_summary?.includes("non-zero exit code")
             )
-        );
+        ) && !(event.type === "NOTIFICATION");
     }
 
     const { data: eventFetchData, isLoading: isEventFetchLoading, isRefetching } = useQuery(

+ 2 - 0
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/types.ts

@@ -36,12 +36,14 @@ const porterAppPreDeployEventMetadataValidator = z.object({
     commit_sha: z.string().optional(),
 });
 export const porterAppNotificationEventMetadataValidator = z.object({
+    id: z.string(),
     app_id: z.string(),
     app_name: z.string(),
     service_name: z.string(),
     app_revision_id: z.string(),
     human_readable_detail: z.string(),
     human_readable_summary: z.string(),
+    timestamp: z.string(),
 });
 export type PorterAppNotification = z.infer<typeof porterAppNotificationEventMetadataValidator>;
 export const porterAppEventValidator = z.discriminatedUnion("type", [

+ 4 - 4
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationExpandedView.tsx

@@ -1,13 +1,13 @@
 import React from "react";
 import styled from "styled-components";
-import { PorterAppNotificationEvent } from "../activity-feed/events/types";
+import { PorterAppNotification } from "../activity-feed/events/types";
 import Text from "components/porter/Text";
 import Spacer from "components/porter/Spacer";
 import document from "assets/document.svg";
 import Button from "components/porter/Button";
 
 type Props = {
-  notification: PorterAppNotificationEvent;
+  notification: PorterAppNotification;
 }
 
 const NotificationExpandedView: React.FC<Props> = ({
@@ -18,11 +18,11 @@ const NotificationExpandedView: React.FC<Props> = ({
       <ExpandedViewContent>
         <Text color="helper">Event ID: {notification.id}</Text>
         <Spacer y={0.5} />
-        <Text size={16}>{notification.metadata.human_readable_summary}</Text>
+        <Text size={16}>{notification.human_readable_summary}</Text>
         <Spacer y={0.5} />
         <Message>
           <img src={document} />
-          {notification.metadata.human_readable_detail}
+          {notification.human_readable_detail}
         </Message>
         <Spacer y={0.5} />
       </ExpandedViewContent>

+ 4 - 4
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationFeed.tsx

@@ -2,18 +2,18 @@ import React, { useState } from "react";
 import styled from "styled-components";
 import NotificationList from "./NotificationList";
 import NotificationExpandedView from "./NotificationExpandedView";
-import { PorterAppNotificationEvent } from "../activity-feed/events/types";
+import { PorterAppNotification } from "../activity-feed/events/types";
 
 type Props = {
-    notifications: PorterAppNotificationEvent[];
+    notifications: PorterAppNotification[];
 };
 
 const NotificationFeed: React.FC<Props> = ({
     notifications,
 }) => {
-    const [selectedNotification, setSelectedNotification] = useState<PorterAppNotificationEvent | null>(null);
+    const [selectedNotification, setSelectedNotification] = useState<PorterAppNotification | undefined>(notifications.length ? notifications[0] : undefined);
 
-    const handleTileClick = (notification: PorterAppNotificationEvent) => {
+    const handleTileClick = (notification: PorterAppNotification) => {
         setSelectedNotification(notification);
     };
 

+ 4 - 4
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationList.tsx

@@ -1,12 +1,12 @@
 import React from "react";
 import styled from "styled-components";
-import { PorterAppNotificationEvent } from "../activity-feed/events/types";
+import { PorterAppNotification } from "../activity-feed/events/types";
 import NotificationTile from "./NotificationTile";
 
 type Props = {
-    onTileClick: (event: PorterAppNotificationEvent) => void;
-    notifications: PorterAppNotificationEvent[];
-    selectedNotification: PorterAppNotificationEvent;
+    onTileClick: (event: PorterAppNotification) => void;
+    notifications: PorterAppNotification[];
+    selectedNotification: PorterAppNotification;
 };
 
 const NotificationList: React.FC<Props> = ({

+ 5 - 5
dashboard/src/main/home/app-dashboard/app-view/tabs/notifications/NotificationTile.tsx

@@ -1,12 +1,12 @@
 import React from "react";
 import styled from "styled-components";
-import { PorterAppNotificationEvent } from "../activity-feed/events/types";
+import { PorterAppNotification } from "../activity-feed/events/types";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
 import { feedDate } from "shared/string_utils";
 
 type Props = {
-  notification: PorterAppNotificationEvent;
+  notification: PorterAppNotification;
   selected: boolean;
   onClick: () => void;
 };
@@ -19,11 +19,11 @@ const NotificationTile: React.FC<Props> = ({
   return (
     <StyledNotificationTile onClick={onClick} selected={selected}>
       <NotificationContent>
-        <Text color="helper">{feedDate(notification.created_at)}</Text>
+        <Text color="helper">{feedDate(notification.timestamp)}</Text>
         <Spacer y={0.5} />
-        <NotificationSummary>{notification.metadata.human_readable_summary}</NotificationSummary>
+        <NotificationSummary>{notification.human_readable_summary}</NotificationSummary>
         <Spacer y={0.5} />
-        <Text color="helper">Service: <ServiceName>{notification.metadata.service_name}</ServiceName></Text>
+        <Text color="helper">Service: <ServiceName>{notification.service_name}</ServiceName></Text>
       </NotificationContent>
     </StyledNotificationTile>
   );

+ 6 - 0
internal/porter_app/notifications/notification.go

@@ -3,7 +3,9 @@ package notifications
 import (
 	"context"
 	"strings"
+	"time"
 
+	"github.com/google/uuid"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/repository"
 	"github.com/porter-dev/porter/internal/telemetry"
@@ -90,6 +92,8 @@ type Notification struct {
 	HumanReadableDetail  string     `json:"human_readable_detail"`
 	HumanReadableSummary string     `json:"human_readable_summary"`
 	Deployment           Deployment `json:"deployment"`
+	Timestamp            time.Time  `json:"timestamp"`
+	ID                   uuid.UUID  `json:"id"`
 }
 
 // agentEventToNotification converts an app event to a notification
@@ -107,6 +111,8 @@ func agentEventToNotification(appEventMetadata AppEventMetadata) Notification {
 		AppRevisionID:       appEventMetadata.AppRevisionID,
 		Deployment:          Deployment{Status: DeploymentStatus_Unknown},
 		HumanReadableDetail: humanReadableDetail,
+		Timestamp:           time.Now().UTC(),
+		ID:                  uuid.New(),
 	}
 	return notification
 }