From a26509d4ae5998fec55e77b5c62b9dd2190467a2 Mon Sep 17 00:00:00 2001 From: Bradley Behnke Date: Thu, 28 Sep 2023 10:53:43 -0700 Subject: [PATCH] feat: edge sidebar and other fixes (#1110) Signed-off-by: bbehnke --- .../partials/EdgeDetails/index.tsx | 119 +++++++++++++++++- .../partials/EdgeDetails/style.css | 5 + .../partials/VertexDetails/index.tsx | 40 +++++- .../partials/VertexDetails/style.css | 5 + .../common/SummaryPageLayout/index.tsx | 4 +- ui/src/components/pages/Pipeline/index.tsx | 2 + .../pages/Pipeline/partials/Graph/index.tsx | 3 +- ui/src/images/map_vertex.png | Bin 0 -> 694 bytes ui/src/images/reduce_vertex.png | Bin 0 -> 677 bytes ui/src/images/sink_vertex.png | Bin 0 -> 985 bytes ui/src/images/source_vertex.png | Bin 0 -> 893 bytes ui/src/types/declarations/pipeline.d.ts | 23 ++++ .../piplelineVertexMetricsFetch.ts | 25 ++-- .../fetchWrappers/piplelineWatermarksFetch.ts | 89 +++++++++++++ ui/yarn.lock | 8 +- 15 files changed, 298 insertions(+), 25 deletions(-) create mode 100644 ui/src/images/map_vertex.png create mode 100644 ui/src/images/reduce_vertex.png create mode 100644 ui/src/images/sink_vertex.png create mode 100644 ui/src/images/source_vertex.png create mode 100644 ui/src/utils/fetchWrappers/piplelineWatermarksFetch.ts diff --git a/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/index.tsx b/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/index.tsx index cf98b8ec66..7168ec9b69 100644 --- a/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/index.tsx +++ b/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/index.tsx @@ -1,15 +1,124 @@ -import React from "react"; +import React, { useState, useEffect, useMemo } from "react"; +import Box from "@mui/material/Box"; +import TableContainer from "@mui/material/TableContainer"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import CircularProgress from "@mui/material/CircularProgress"; +import { usePiplelineWatermarksFetch } from "../../../../../utils/fetchWrappers/piplelineWatermarksFetch"; +import { PipelineWatermarks } from "../../../../../types/declarations/pipeline"; import "./style.css"; - export interface EdgeDetailsProps { - namespaceId: string; // TODO + namespaceId: string; + pipelineId: string; edgeId: string; } -export function EdgeDetails({ namespaceId, edgeId }: EdgeDetailsProps) { +export function EdgeDetails({ + namespaceId, + pipelineId, + edgeId, +}: EdgeDetailsProps) { + const [edgeWatermarks, setEdgeWatermarks] = useState< + PipelineWatermarks | undefined + >(); + const { data, loading, error } = usePiplelineWatermarksFetch({ + namespace: namespaceId, + pipeline: pipelineId, + }); + + useEffect(() => { + if (!data) { + return; + } + setEdgeWatermarks(data.find((metric) => metric.edgeId === edgeId)); + }, [data, edgeId]); + + const content = useMemo(() => { + if (loading) { + return ( + + + + ); + } + if (error) { + return ( + + {`Error loading processing rates: ${error}`} + + ); + } + if (!data || !edgeWatermarks) { + return ( + + {`No resources found.`} + + ); + } + return ( + + + + + Partition + Watermark + + + + {!edgeWatermarks.watermarks.length && ( + + + No watermarks found + + + )} + {!!edgeWatermarks.watermarks.length && + edgeWatermarks.watermarks.map((watermark) => ( + + {watermark.partition} + {`${watermark.watermark} (${watermark.formattedWatermark})`} + + ))} + +
+
+ ); + }, [data, loading, error, edgeWatermarks]); + return ( -
TODO Edge details
+ + + {`${edgeId} Edge`} + + {content} + ); } diff --git a/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/style.css b/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/style.css index e69de29bb2..2039ed9093 100644 --- a/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/style.css +++ b/ui/src/components/common/SlidingSidebar/partials/EdgeDetails/style.css @@ -0,0 +1,5 @@ +.edge-details-header-text { + font-size: 1.25rem; + font-style: normal; + font-weight: 500; +} \ No newline at end of file diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx index 8f6909f466..2072342b04 100644 --- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx +++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx @@ -6,6 +6,10 @@ import { SpecEditor } from "./partials/SpecEditor"; import { ProcessingRates } from "./partials/ProcessingRates"; import { K8sEvents } from "../K8sEvents"; import { Pods } from "../../../../pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods"; +import sourceIcon from "../../../../../images/source_vertex.png"; +import sinkIcon from "../../../../../images/sink_vertex.png"; +import mapIcon from "../../../../../images/map_vertex.png"; +import reducIcon from "../../../../../images/reduce_vertex.png"; import "./style.css"; @@ -17,7 +21,8 @@ const K8S_EVENTS_TAB_INDEX = 3; export enum VertexType { SOURCE, SINK, - PROCESSOR, + MAP, + REDUCE, } export interface VertexDetailsProps { @@ -42,8 +47,10 @@ export function VertexDetails({ const foundSpec = vertexSpecs.find((spec) => spec.name === vertexId); if (foundSpec.source) { setVertexType(VertexType.SOURCE); + } else if (foundSpec.udf && foundSpec.groupBy) { + setVertexType(VertexType.REDUCE); } else if (foundSpec.udf) { - setVertexType(VertexType.PROCESSOR); + setVertexType(VertexType.MAP); } else if (foundSpec.sink) { setVertexType(VertexType.SINK); } @@ -61,24 +68,51 @@ export function VertexDetails({ const headerContainerStyle = { display: "flex", flexDirection: "row", + alignItems: "center", }; const textClass = "vertex-details-header-text"; switch (vertexType) { case VertexType.SOURCE: return ( + source vertex Input Vertex ); - case VertexType.PROCESSOR: + case VertexType.REDUCE: return ( + reduce vertex + Processor Vertex + + ); + case VertexType.MAP: + return ( + + map vertex Processor Vertex ); case VertexType.SINK: return ( + sink vertex Sink Vertex ); diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/style.css b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/style.css index 33209f1b76..48c950e82e 100644 --- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/style.css +++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/style.css @@ -2,6 +2,11 @@ font-size: 1.25rem; font-style: normal; font-weight: 500; + margin-left: 1rem; +} + +.vertex-details-header-icon { + width: 2.25rem; } .vertex-details-tab-panel { diff --git a/ui/src/components/common/SummaryPageLayout/index.tsx b/ui/src/components/common/SummaryPageLayout/index.tsx index 7a0d788c5b..5ce430c0a1 100644 --- a/ui/src/components/common/SummaryPageLayout/index.tsx +++ b/ui/src/components/common/SummaryPageLayout/index.tsx @@ -41,6 +41,7 @@ export interface SummaryPageLayoutProps { collapsedText?: string; summarySections: SummarySection[]; contentComponent: React.ReactNode; + contentPadding?: boolean; } const SUMMARY_HEIGHT = "6.5625rem"; @@ -185,6 +186,7 @@ export function SummaryPageLayout({ collapsedText = "Details", summarySections, contentComponent, + contentPadding = true, }: SummaryPageLayoutProps) { const [collapsed, setCollapsed] = useState(collapsable && defaultCollapsed); const sumaryRef = useRef(); @@ -297,7 +299,7 @@ export function SummaryPageLayout({ diff --git a/ui/src/components/pages/Pipeline/index.tsx b/ui/src/components/pages/Pipeline/index.tsx index e1905bf0c8..f57caffeb2 100644 --- a/ui/src/components/pages/Pipeline/index.tsx +++ b/ui/src/components/pages/Pipeline/index.tsx @@ -133,6 +133,8 @@ export function Pipeline() { return ( `i}@s}>buufuKXrL$5-XDmhV_F1YmW%CIO)ONXPz^uWPGBUI~-oneHPGBvOjKHz5WUPc{p(w<) z-rPFaYR%YiI&f~^f^+AlJpb7Hg>D#*wal*Fm&f4u8G_S4NJ`?86*nKkd-+`c-fTP= zzxQ~3cu%y`!6-w8&5eoc_5XS-DcVBKgCkjjDllu}&RK7WG=+Cu(GPaex#kK}QX zoz96_{`G}^_M}v1Ey{a|#C(zoPS0S1QHDNzKbAdHpfJCKSZf+A2u1n2Y@@Pd?*b)=G85%CL-F3_7NVGL7@h8zV(P>XYOQBH{BNn#s}<)Fqp+4>3>I@~Rz#Yq{rXSqhPWef-s*_SKcw&?T0Ynq(>`NB-JG%fZZr z2>V#Xb9BEzI`Pw{^oNx&=I9Bdk{}9?)tu1887qPj zINDV6D{9E{t8Uhb5OjjEV=xNCAuJ;|Ju8d(KX^Z`ZyEs^9*F8;NX14X!pP0s*X8ONK@HX(D!UR_;mX50G1m2tcYZ4<1Z$}xa=hes8WUnXo8 zF@z`}6E+!Jf})`&h{MpG+XxmVD~6r)wZm#fW=6VPn)o$|knX{}n3$ZDhX$?D zxzP341qm|K(@i6WwA6<+69TcgT%sbMioK zS?P+1Ajpfvv=D1V451t{+Jusp$jnGX(WQ$hymSFRzdvBp|K2MSd=Ji_$d>um^X4t| zqU<56Dh=Q82tE({u_G}N+pUSK>^k~hN>6H4rWYj-Q79-xJ|V*6`znN3aYXbZt-nwh zSBZN5LiQjMMV@*!=!Gguf@bDsWrdv+p`e2xAq{Ph!rD`K-EAH6A__&jpnQr{m7&n{ z5QieNBa0?TU0SvC>P0!J3A$49R6N;w{D{UT6eJ$3v3c;Ay49d$k=rXWty%8Ozawi2FZA;h_!S=9N^ z@Q~NDl3zSY%!m%b2n@~hi}P1{8r`p>o9ck_i<>c^!&@H4#mFT>cw!b55Ed=JsElE% z=yu<2hfWMlVYN0vvlQ_;7Az#RjP+u8POTBtCQ`zcv0nZF5y_1Kcu?*%00000NkvXX Hu0mjfqVvhc literal 0 HcmV?d00001 diff --git a/ui/src/images/source_vertex.png b/ui/src/images/source_vertex.png new file mode 100644 index 0000000000000000000000000000000000000000..db70a082afbc4983c5ae58593c64e17c9a451018 GIT binary patch literal 893 zcmV-@1A_dCP)i?Uk${W{<+M$8aN~+CRROy&z^9i^hprqjB zbjLXpU%sR7*|ZdxFp&3r35W5$_=?fDb2!Z=Z(#ok#uV)nz)1$n``$T+{*`KePR4<* z))vPnC6Q(GAD8y@BMZZ9JH4=mes2x4Hy<(*&a&s97WsCp>ms}IvwQ3z733Y^F-AY? zL$*%NtU^Zg=rBY(w_QGko~Fw9Rg-n59a96>cR*6yjJ})0@T*zKNU9EvGmE?rKcMl! zdmgYoH!3-xDYCPHwVAdOu&itqMv9-|i5b3cO?6V3x6Sh7{oZQGN*IU+pBlIpt3#~j zbT?J;x$5f~R-xiGa41jTa7>IAbPC^S->Dy@d*Rk$1<0Ne$E0_b$ zpv2t@u?dEf*^@SqDS`N+G*+{|=xJ4~OdL^JkoH { return { partition: index, - oneM: item.processingRates["1m"] || 0, - fiveM: item.processingRates["5m"] || 0, - fifteenM: item.processingRates["15m"] || 0, + oneM: + item.processingRates && item.processingRates["1m"] + ?item.processingRates["1m"].toFixed(2) + : 0, + fiveM: + item.processingRates && item.processingRates["5m"] + ? item.processingRates["5m"].toFixed(2) + : 0, + fifteenM: + item.processingRates && item.processingRates["15m"] + ? item.processingRates["15m"].toFixed(2) + : 0, }; }), - } + }; }); }; @@ -84,13 +93,7 @@ export const usePiplelineVertexMetricsFetch = ({ }); return; } - }, [ - data, - loading, - error, - loadOnRefresh, - options, - ]); + }, [data, loading, error, loadOnRefresh, options]); return results; }; diff --git a/ui/src/utils/fetchWrappers/piplelineWatermarksFetch.ts b/ui/src/utils/fetchWrappers/piplelineWatermarksFetch.ts new file mode 100644 index 0000000000..3d6c01aa1a --- /dev/null +++ b/ui/src/utils/fetchWrappers/piplelineWatermarksFetch.ts @@ -0,0 +1,89 @@ +import { useEffect, useState } from "react"; +import { useFetch, Options } from "./fetch"; +import { + PipelineWatermarks, + PipelineWatermarksFetchResult, + PipelineWatermarksFetchProps, +} from "../../types/declarations/pipeline"; + +const rawDataToWatermarks = ( + rawData: any +): PipelineWatermarks[] | undefined => { + if (!rawData) { + return undefined; + } + return rawData.map((item: any) => { + return { + edgeId: item.edge, + watermarks: item.watermarks.map((watermark: number, index: number) => { + return { + partition: index, + watermark, + formattedWatermark: new Date(watermark).toISOString(), + } + }), + }; + }); +}; + +export const usePiplelineWatermarksFetch = ({ + namespace, + pipeline, + loadOnRefresh = false, +}: PipelineWatermarksFetchProps) => { + const [results, setResults] = useState({ + data: undefined, + loading: true, + error: undefined, + }); + const [options] = useState({ + skip: false, + requestKey: "", + }); + const { data, loading, error } = useFetch( + `/api/v1_1/namespaces/${namespace}/pipelines/${pipeline}/watermarks`, + undefined, + options + ); + + useEffect(() => { + if (loading) { + if (options?.requestKey === "" || loadOnRefresh) { + // Only set loading true when first load or when loadOnRefresh is true + setResults({ + data: undefined, + loading: true, + error: undefined, + }); + } + return; + } + if (error) { + setResults({ + data: undefined, + loading: false, + error: error, + }); + return; + } + if (data?.errMsg) { + setResults({ + data: undefined, + loading: false, + error: data?.errMsg, + }); + return; + } + if (data) { + const result = rawDataToWatermarks(data.data); + setResults({ + data: result, + loading: false, + error: undefined, + }); + return; + } + }, [data, loading, error, loadOnRefresh, options]); + + return results; +}; diff --git a/ui/yarn.lock b/ui/yarn.lock index 37bf7559a8..0151a1906d 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1767,14 +1767,14 @@ "@monaco-editor/loader@^1.3.3": version "1.3.3" - resolved "https://registry.npmjs.intuit.com:443/artifactory/api/npm/npm-intuit/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca" + resolved "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca" integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q== dependencies: state-local "^1.0.6" "@monaco-editor/react@^4.5.2": version "4.5.2" - resolved "https://registry.npmjs.intuit.com:443/artifactory/api/npm/npm-intuit/@monaco-editor/react/-/react-4.5.2.tgz#e8cc802203f729b423a998ea6fcb466604d61258" + resolved "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.2.tgz#e8cc802203f729b423a998ea6fcb466604d61258" integrity sha512-emcWu6vg1OpXPiYll4aPOaXe8bwYB4UaaNTwtArFLgMoNGBzRZb2Xn0Bra2HMIFM7QLgs7fCGunHO5LkfT2LBA== dependencies: "@monaco-editor/loader" "^1.3.3" @@ -9997,7 +9997,7 @@ stackframe@^1.3.4: state-local@^1.0.6: version "1.0.7" - resolved "https://registry.npmjs.intuit.com:443/artifactory/api/npm/npm-intuit/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== statuses@2.0.1, statuses@^2.0.0: @@ -11295,7 +11295,7 @@ yaml@^2.1.1: yaml@^2.3.2: version "2.3.2" - resolved "https://registry.npmjs.intuit.com:443/artifactory/api/npm/npm-intuit/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== yargs-parser@^20.2.2: