소스 검색

match on metadata type

Feroze Mohideen 2 년 전
부모
커밋
9df7e90283

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

@@ -14,7 +14,7 @@ import { feedDate } from "shared/string_utils";
 import Pagination from "components/porter/Pagination";
 import _ from "lodash";
 import Button from "components/porter/Button";
-import { PorterAppEvent, PorterAppEventType, porterAppEventValidator } from "./events/types";
+import { PorterAppEvent, porterAppEventValidator } from "./events/types";
 import { z } from "zod";
 
 type Props = {
@@ -64,7 +64,7 @@ const ActivityFeed: React.FC<Props> = ({ appName, deploymentTargetId, currentClu
     };
 
     const getLatestDeployEventIndex = () => {
-        const deployEvents = events.filter((event) => event.type === PorterAppEventType.DEPLOY);
+        const deployEvents = events.filter((event) => event.type === 'DEPLOY');
         if (deployEvents.length === 0) {
             return -1;
         }

+ 2 - 2
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/cards/BuildEventCard.tsx

@@ -14,7 +14,7 @@ import Icon from "components/porter/Icon";
 import { getDuration, getStatusColor, getStatusIcon, triggerWorkflow } from '../utils';
 import { StyledEventCard } from "./EventCard";
 import document from "assets/document.svg";
-import { PorterAppBuildEvent, PorterAppEvent } from "../types";
+import { PorterAppBuildEvent } from "../types";
 import { useLatestRevision } from "main/home/app-dashboard/app-view/LatestRevisionContext";
 
 type Props = {
@@ -26,7 +26,7 @@ type Props = {
 
 const BuildEventCard: React.FC<Props> = ({ event, appName, projectId, clusterId }) => {
   const { porterApp } = useLatestRevision();
-  const renderStatusText = (event: PorterAppEvent) => {
+  const renderStatusText = (event: PorterAppBuildEvent) => {
     switch (event.status) {
       case "SUCCESS":
         return <Text color={getStatusColor(event.status)}>Build succeeded</Text>;

+ 6 - 6
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/cards/EventCard.tsx

@@ -5,7 +5,7 @@ import BuildEventCard from "./BuildEventCard";
 import PreDeployEventCard from "./PreDeployEventCard";
 import AppEventCard from "./AppEventCard";
 import DeployEventCard from "./DeployEventCard";
-import { PorterAppAppEvent, PorterAppBuildEvent, PorterAppDeployEvent, PorterAppEvent, PorterAppEventType, } from "../types";
+import { PorterAppEvent } from "../types";
 import { match } from "ts-pattern";
 
 type Props = {
@@ -18,11 +18,11 @@ type Props = {
 };
 
 const EventCard: React.FC<Props> = ({ event, deploymentTargetId, isLatestDeployEvent, projectId, clusterId, appName }) => {
-  return match(event.type)
-    .with(PorterAppEventType.APP_EVENT, () => <AppEventCard event={event as PorterAppAppEvent} deploymentTargetId={deploymentTargetId} projectId={projectId} clusterId={clusterId} appName={appName} />)
-    .with(PorterAppEventType.BUILD, () => <BuildEventCard event={event as PorterAppBuildEvent} projectId={projectId} clusterId={clusterId} appName={appName} />)
-    .with(PorterAppEventType.DEPLOY, () => <DeployEventCard event={event as PorterAppDeployEvent} appName={appName} showServiceStatusDetail={isLatestDeployEvent} />)
-    .with(PorterAppEventType.PRE_DEPLOY, () => <PreDeployEventCard event={event} appName={appName} projectId={projectId} clusterId={clusterId} />)
+  return match(event)
+    .with({ type: "APP_EVENT" }, (ev) => <AppEventCard event={ev} deploymentTargetId={deploymentTargetId} projectId={projectId} clusterId={clusterId} appName={appName} />)
+    .with({ type: "BUILD" }, (ev) => <BuildEventCard event={ev} projectId={projectId} clusterId={clusterId} appName={appName} />)
+    .with({ type: "DEPLOY" }, (ev) => <DeployEventCard event={ev} appName={appName} showServiceStatusDetail={isLatestDeployEvent} />)
+    .with({ type: "PRE_DEPLOY" }, (ev) => <PreDeployEventCard event={ev} appName={appName} projectId={projectId} clusterId={clusterId} />)
     .exhaustive();
 };
 

+ 3 - 3
dashboard/src/main/home/app-dashboard/app-view/tabs/activity-feed/events/cards/PreDeployEventCard.tsx

@@ -15,11 +15,11 @@ import { getDuration, getStatusColor, getStatusIcon, triggerWorkflow } from '../
 import { StyledEventCard } from "./EventCard";
 import Link from "components/porter/Link";
 import document from "assets/document.svg";
-import { PorterAppEvent } from "../types";
+import { PorterAppPreDeployEvent } from "../types";
 import { useLatestRevision } from "main/home/app-dashboard/app-view/LatestRevisionContext";
 
 type Props = {
-  event: PorterAppEvent;
+  event: PorterAppPreDeployEvent;
   appName: string;
   projectId: number;
   clusterId: number;
@@ -28,7 +28,7 @@ type Props = {
 const PreDeployEventCard: React.FC<Props> = ({ event, appName, projectId, clusterId }) => {
   const { porterApp } = useLatestRevision();
 
-  const renderStatusText = (event: PorterAppEvent) => {
+  const renderStatusText = (event: PorterAppPreDeployEvent) => {
     switch (event.status) {
       case "SUCCESS":
         return <Text color={getStatusColor(event.status)}>Pre-deploy succeeded</Text>;

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

@@ -1,11 +1,7 @@
 import { z } from "zod";
 
-export enum PorterAppEventType {
-    BUILD = "BUILD",
-    DEPLOY = "DEPLOY",
-    APP_EVENT = "APP_EVENT",
-    PRE_DEPLOY = "PRE_DEPLOY",
-}
+export type PorterAppEventType = 'BUILD' | 'DEPLOY' | 'APP_EVENT' | 'PRE_DEPLOY';
+
 const porterAppAppEventMetadataValidator = z.object({
     namespace: z.string(),
     summary: z.string(),
@@ -35,61 +31,53 @@ const porterAppPreDeployEventMetadataValidator = z.object({
     start_time: z.string(),
     end_time: z.string().optional(),
 });
-export const porterAppEventValidator = z.object({
-    id: z.string(),
-    created_at: z.string(),
-    updated_at: z.string(),
-    status: z.string().optional().default(""),
-    type: z.nativeEnum(PorterAppEventType),
-    type_external_source: z.string().optional().default(""),
-    porter_app_id: z.number(),
-    metadata: z.union([
-        porterAppAppEventMetadataValidator,
-        porterAppDeployEventMetadataValidator,
-        porterAppBuildEventMetadataValidator,
-        porterAppPreDeployEventMetadataValidator,
-    ]).optional(),
-}).refine((data) => {
-    if (data.type === PorterAppEventType.APP_EVENT) {
-        return porterAppAppEventMetadataValidator.safeParse(data.metadata).success;
-    }
-    if (data.type === PorterAppEventType.DEPLOY) {
-        return porterAppDeployEventMetadataValidator.safeParse(data.metadata).success;
-    }
-    if (data.type === PorterAppEventType.BUILD) {
-        return porterAppBuildEventMetadataValidator.safeParse(data.metadata).success;
-    }
-    if (data.type === PorterAppEventType.PRE_DEPLOY) {
-        return porterAppPreDeployEventMetadataValidator.safeParse(data.metadata).success;
-    }
-    return true;
-});
+export const porterAppEventValidator = z.discriminatedUnion("type", [
+    z.object({
+        id: z.string(),
+        created_at: z.string(),
+        updated_at: z.string(),
+        status: z.string().optional().default(""),
+        type: z.literal("BUILD"),
+        type_external_source: z.string().optional().default(""),
+        porter_app_id: z.number(),
+        metadata: porterAppBuildEventMetadataValidator
+    }),
+    z.object({
+        id: z.string(),
+        created_at: z.string(),
+        updated_at: z.string(),
+        status: z.string().optional().default(""),
+        type: z.literal("DEPLOY"),
+        type_external_source: z.string().optional().default(""),
+        porter_app_id: z.number(),
+        metadata: porterAppDeployEventMetadataValidator
+    }),
+    z.object({
+        id: z.string(),
+        created_at: z.string(),
+        updated_at: z.string(),
+        status: z.string().optional().default(""),
+        type: z.literal("PRE_DEPLOY"),
+        type_external_source: z.string().optional().default(""),
+        porter_app_id: z.number(),
+        metadata: porterAppPreDeployEventMetadataValidator
+    }),
+    z.object({
+        id: z.string(),
+        created_at: z.string(),
+        updated_at: z.string(),
+        status: z.string().optional().default(""),
+        type: z.literal("APP_EVENT"),
+        type_external_source: z.string().optional().default(""),
+        porter_app_id: z.number(),
+        metadata: porterAppAppEventMetadataValidator
+    }),
+]);
 
 export const getPorterAppEventsValidator = z.array(porterAppEventValidator).optional().default([]);
 
 export type PorterAppEvent = z.infer<typeof porterAppEventValidator>;
-// TODO: figure out how to type this easier
-export type PorterAppAppEvent = Omit<PorterAppEvent, 'metadata'> & { type: PorterAppEventType.APP_EVENT, metadata: z.infer<typeof porterAppAppEventMetadataValidator> };
-export type PorterAppDeployEvent = Omit<PorterAppEvent, 'metadata'> & { type: PorterAppEventType.DEPLOY, metadata: z.infer<typeof porterAppDeployEventMetadataValidator> };
-export type PorterAppBuildEvent = Omit<PorterAppEvent, 'metadata'> & { type: PorterAppEventType.BUILD, metadata: z.infer<typeof porterAppBuildEventMetadataValidator> };
-// interface PorterAppServiceDeploymentMetadata {
-//     status: string;
-//     external_uri: string;
-//     type: string;
-// }
-// export interface PorterAppDeployEvent extends PorterAppEvent {
-//     type: PorterAppEventType.DEPLOY;
-//     metadata: {
-//         image_tag: string;
-//         revision: number;
-//         service_deployment_metadata: Record<string, PorterAppServiceDeploymentMetadata>;
-//     };
-// }
-// export interface PorterAppAppEvent extends PorterAppEvent {
-//     type: PorterAppEventType.APP_EVENT;
-//     metadata: {
-//         image_tag: string;
-//         revision: number;
-//         service_deployment_metadata: Record<string, PorterAppServiceDeploymentMetadata>;
-//     };
-// }
+export type PorterAppBuildEvent = PorterAppEvent & { type: 'BUILD' };
+export type PorterAppDeployEvent = PorterAppEvent & { type: 'DEPLOY' };
+export type PorterAppPreDeployEvent = PorterAppEvent & { type: 'PRE_DEPLOY' };
+export type PorterAppAppEvent = PorterAppEvent & { type: 'APP_EVENT' };