diff --git a/frontend/webapp/containers/main/overview/data.flow/index.tsx b/frontend/webapp/containers/main/overview/data.flow/index.tsx index d66616ba9..730fefe32 100644 --- a/frontend/webapp/containers/main/overview/data.flow/index.tsx +++ b/frontend/webapp/containers/main/overview/data.flow/index.tsx @@ -4,7 +4,12 @@ import { OverviewDataFlowWrapper } from './styled'; import { ROUTES, getMainContainerLanguage } from '@/utils'; import { useRouter, useSearchParams } from 'next/navigation'; import { KeyvalDataFlow, KeyvalLoader } from '@/design.system'; -import { useActions, useDestinations, useSources } from '@/hooks'; +import { + useActions, + useDestinations, + useOverviewMetrics, + useSources, +} from '@/hooks'; import { buildFlowNodesAndEdges } from '@keyval-dev/design-system'; interface FlowNode { @@ -42,6 +47,9 @@ export function DataFlowContainer() { const useSearch = useSearchParams(); const intervalId = useRef(); + + const { metrics } = useOverviewMetrics(); + useEffect(() => { if (!sources || !destinationList || !actions) return; @@ -61,9 +69,32 @@ export function DataFlowContainer() { const conditions = source?.instrumented_application_details?.conditions || []; + const currentSourceMetrics = metrics?.sources?.find( + (metric) => + metric.name === source.name && + metric.namespace === source.namespace && + metric.kind === source.kind + ); + + if (!currentSourceMetrics) { + return { + ...source, + conditions, + languages: + languages.length > 0 + ? languages + : [{ language: 'default', container_name: '' }], + }; + } + + const data_transfer = formatBytes(currentSourceMetrics?.throughput); + return { ...source, conditions, + metrics: { + data_transfer, + }, languages: languages.length > 0 ? languages @@ -71,14 +102,38 @@ export function DataFlowContainer() { }; }); + const mapDestinations = destinationList.map((destination) => { + const currentDestinationMetrics = metrics?.destinations?.find( + (metric) => metric.id === destination.id + ); + + if (!currentDestinationMetrics) { + return destination; + } + + const data_transfer = formatBytes( + currentDestinationMetrics?.throughput || + currentDestinationMetrics?.throughput === 0 + ? currentDestinationMetrics?.throughput + : 0 + ); + + return { + ...destination, + metrics: { + data_transfer, + }, + }; + }); + const { nodes, edges } = buildFlowNodesAndEdges( mapSources, - destinationList, + mapDestinations, filteredActions ); setNodes(nodes); setEdges(edges); - }, [actions, destinationList, sources]); + }, [actions, destinationList, sources, metrics]); useEffect(() => { const pullData = useSearch.get(POLL_DATA); @@ -126,3 +181,11 @@ export function DataFlowContainer() { ); } + +function formatBytes(bytes: number): string { + const sizes = ['Bytes', 'KB/s', 'MB/s', 'GB/s', 'TB/s']; + if (bytes === 0) return '0 KB/s'; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + const value = bytes / Math.pow(1024, i); + return `${value.toFixed(2)} ${sizes[i]}`; +} diff --git a/frontend/webapp/hooks/index.tsx b/frontend/webapp/hooks/index.tsx index 25b8aaeb8..f0eae751d 100644 --- a/frontend/webapp/hooks/index.tsx +++ b/frontend/webapp/hooks/index.tsx @@ -7,3 +7,4 @@ export * from './destinations'; export * from './actions'; export * from './useNotify'; export * from './useSSE'; +export * from './useOverviewMetrics'; diff --git a/frontend/webapp/hooks/useOverviewMetrics.ts b/frontend/webapp/hooks/useOverviewMetrics.ts new file mode 100644 index 000000000..527082aea --- /dev/null +++ b/frontend/webapp/hooks/useOverviewMetrics.ts @@ -0,0 +1,10 @@ +import { useQuery } from 'react-query'; +import { getOverviewMetrics } from '@/services/metrics'; + +export function useOverviewMetrics() { + const { data: metrics } = useQuery([], getOverviewMetrics, { + refetchInterval: 5000, + }); + + return { metrics }; +} diff --git a/frontend/webapp/package.json b/frontend/webapp/package.json index 5fa5b8771..8fd8feaf7 100644 --- a/frontend/webapp/package.json +++ b/frontend/webapp/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@focus-reactive/react-yaml": "^1.1.2", - "@keyval-dev/design-system": "^2.0.5", + "@keyval-dev/design-system": "^2.1.0", "@next/font": "^13.4.7", "@reduxjs/toolkit": "^2.2.1", "@svgr/webpack": "^6.2.1", diff --git a/frontend/webapp/services/metrics.ts b/frontend/webapp/services/metrics.ts new file mode 100644 index 000000000..abc4867fd --- /dev/null +++ b/frontend/webapp/services/metrics.ts @@ -0,0 +1,6 @@ +import { API } from '@/utils/constants'; +import { get } from './api'; + +export async function getOverviewMetrics() { + return await get(API.OVERVIEW_METRICS); +} diff --git a/frontend/webapp/utils/constants/urls.tsx b/frontend/webapp/utils/constants/urls.tsx index c340e33b3..4c9b9de38 100644 --- a/frontend/webapp/utils/constants/urls.tsx +++ b/frontend/webapp/utils/constants/urls.tsx @@ -18,6 +18,7 @@ const API = { ACTIONS: `${BASE_URL}/actions`, DELETE_ACTION: (type: string, id: string) => `${BASE_URL}/actions/types/${type}/${id}`, + OVERVIEW_METRICS: `${BASE_URL}/metrics/overview`, }; const QUERIES = { diff --git a/frontend/webapp/yarn.lock b/frontend/webapp/yarn.lock index f267697f6..1ac752a77 100644 --- a/frontend/webapp/yarn.lock +++ b/frontend/webapp/yarn.lock @@ -1482,10 +1482,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@keyval-dev/design-system@^2.0.5": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@keyval-dev/design-system/-/design-system-2.0.6.tgz#bdd31fdc3c2921a1a7b171291bdd0fd8e317122b" - integrity sha512-jul2Ypi1c1vl1PfAoBnmZyd23mzFlEw0DdYWcGCg48JDunKEonS5V+JzMvCWccpr08zCqqT47mz++8QoSRYjZQ== +"@keyval-dev/design-system@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@keyval-dev/design-system/-/design-system-2.1.0.tgz#9b7b35449d7843e4ff06c81716dab1e447e0b8fb" + integrity sha512-JmCdW63WAzpBOnDZFgjzwrQaaKoONjuNAR0r8fLxPXc7vYTjnDmgPLqdo4t5Ha8GLCSSvgf+cNvQW/KfPJptDw== dependencies: "@focus-reactive/react-yaml" "^1.1.2" "@svgr/core" "^8.0.0"