From 4d64dd4b042e54c93507dd935868978047ca6346 Mon Sep 17 00:00:00 2001 From: AmitUp9 <96980485+AmitUp9@users.noreply.github.com> Date: Tue, 21 Jun 2022 14:54:56 +0300 Subject: [PATCH] TRA-4602_timeline bar charts to traffic statistics (#1159) * pie chart for protocols and methods by requests and volume * protocols legend * timeline bar chart component created * timeline can view requests and volume * sorting the bra charts by timestemp * disable view of <1% pieces in pie * space added to the end of the file * package.json update * cr fixes * remove spave * remove unnecessary react fragment Co-authored-by: Liraz Yehezkel --- .../TimelineBarChart.module.sass | 4 + .../TimelineBarChart/TimelineBarChart.tsx | 83 +++++++++++++++++++ .../TrafficPieChart/TrafficPieChart.tsx | 8 +- .../TrafficStatsModal/TrafficStatsModal.tsx | 43 ++++++---- ui/src/App.tsx | 2 +- ui/src/helpers/api.js | 7 +- 6 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.module.sass create mode 100644 ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx diff --git a/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.module.sass b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.module.sass new file mode 100644 index 000000000..36e9eba94 --- /dev/null +++ b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.module.sass @@ -0,0 +1,4 @@ +.barChartContainer + width: 100% + display: flex + justify-content: center diff --git a/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx new file mode 100644 index 000000000..7f559bc81 --- /dev/null +++ b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx @@ -0,0 +1,83 @@ +import styles from "./TimelineBarChart.module.sass"; +import { StatsMode } from "../TrafficStatsModal" +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { + BarChart, + Bar, + XAxis, + YAxis, + Tooltip, + Legend +} from "recharts"; +import { Utils } from "../../../../helpers/Utils"; + +interface TimelineBarChartProps { + timeLineBarChartMode: string; + data: any; +} + +export const TimelineBarChart: React.FC = ({ timeLineBarChartMode, data }) => { + const [protocolStats, setProtocolStats] = useState([]); + const [protocolsNamesAndColors, setProtocolsNamesAndColors] = useState([]); + + const padTo2Digits = useCallback((num) => { + return String(num).padStart(2, '0'); + }, []) + + const getHoursAndMinutes = useCallback((protocolTimeKey) => { + const time = new Date(protocolTimeKey) + const hoursAndMinutes = padTo2Digits(time.getHours()) + ':' + padTo2Digits(time.getMinutes()); + return hoursAndMinutes; + }, [padTo2Digits]) + + const creatUniqueObjArray = useCallback((objArray) => { + return [ + ...new Map(objArray.map((item) => [item["name"], item])).values(), + ]; + }, []) + + useEffect(() => { + if (!data) return; + const protocolsBarsData = []; + const prtcNames = []; + data.map(protocolObj => { + let obj: { [k: string]: any } = {}; + obj.timestamp = getHoursAndMinutes(protocolObj.timestamp); + protocolObj.protocols.forEach(protocol => { + obj[`${protocol.name}`] = protocol[StatsMode[timeLineBarChartMode]]; + prtcNames.push({ name: protocol.name, color: protocol.color }); + }) + protocolsBarsData.push(obj); + }) + const uniqueObjArray = creatUniqueObjArray(prtcNames); + protocolsBarsData.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1); + setProtocolStats(protocolsBarsData); + setProtocolsNamesAndColors(uniqueObjArray); + }, [data, timeLineBarChartMode, setProtocolStats, setProtocolsNamesAndColors, creatUniqueObjArray, getHoursAndMinutes]) + + const bars = useMemo(() => protocolsNamesAndColors.map((protocolToDIsplay) => { + return + }), [protocolsNamesAndColors]) + + return ( +
+ + + timeLineBarChartMode === "VOLUME" ? Utils.humanFileSize(value) : value} /> + timeLineBarChartMode === "VOLUME" ? Utils.humanFileSize(value) : value + " Requests"} /> + + {bars} + +
+ ); +} diff --git a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx b/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx index 770acf30e..37d498240 100644 --- a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx +++ b/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx @@ -2,11 +2,7 @@ import React, {useEffect, useMemo, useState} from "react"; import styles from "./TrafficPieChart.module.sass"; import {Cell, Legend, Pie, PieChart, Tooltip} from "recharts"; import {Utils} from "../../../../helpers/Utils"; - -enum PieChartMode { - REQUESTS = "entriesCount", - VOLUME = "volumeSizeBytes" -} +import {StatsMode as PieChartMode} from "../TrafficStatsModal" const COLORS = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000']; @@ -24,6 +20,8 @@ const renderCustomizedLabel = ({ const x = cx + radius * Math.cos(-midAngle * RADIAN); const y = cy + radius * Math.sin(-midAngle * RADIAN); + if (Number((percent * 100).toFixed(0)) <= 1) return; + return ( void; - getTrafficStatsDataApi: () => Promise + getPieStatsDataApi: () => Promise + getTimelineStatsDataApi: () => Promise } -export const TrafficStatsModal: React.FC = ({ isOpen, onClose, getTrafficStatsDataApi }) => { +export const TrafficStatsModal: React.FC = ({ isOpen, onClose, getPieStatsDataApi, getTimelineStatsDataApi }) => { const modes = Object.keys(StatsMode).filter(x => !(parseInt(x) >= 0)); const [statsMode, setStatsMode] = useState(modes[0]); - const [statsData, setStatsData] = useState(null); + const [pieStatsData, setPieStatsData] = useState(null); + const [timelineStatsData, setTimelineStatsData] = useState(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if(isOpen && getTrafficStatsDataApi) { + if (isOpen && getPieStatsDataApi) { (async () => { try { setIsLoading(true); - const data = await getTrafficStatsDataApi(); - setStatsData(data); + const pieData = await getPieStatsDataApi(); + setPieStatsData(pieData); + const timelineData = await getTimelineStatsDataApi(); + setTimelineStatsData(timelineData); } catch (e) { console.error(e) } finally { @@ -51,7 +56,7 @@ export const TrafficStatsModal: React.FC = ({ isOpen, on } })() } - }, [isOpen, getTrafficStatsDataApi]) + }, [isOpen, getPieStatsDataApi, getTimelineStatsDataApi, setPieStatsData, setTimelineStatsData]) return ( = ({ isOpen, on
- close onClose()} style={{ cursor: "pointer", userSelect: "none" }}/> + close onClose()} style={{ cursor: "pointer", userSelect: "none" }} />
Traffic Statistics
- Breakdown By + Breakdown By
- {isLoading ?
+
+ {isLoading ?
spinner -
: } +
: +
+ + +
} +
diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 7754ecc23..303eec47f 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -36,7 +36,7 @@ const App = () => { openModal={oasModalOpen} handleCloseModal={() => setOasModalOpen(false)} />} - setTrafficStatsModalOpen(false)} getTrafficStatsDataApi={api.getStats}/> + setTrafficStatsModalOpen(false)} getPieStatsDataApi={api.getPieStats} getTimelineStatsDataApi={api.getTimelineStats}/> diff --git a/ui/src/helpers/api.js b/ui/src/helpers/api.js index b159f9064..f6edfb448 100644 --- a/ui/src/helpers/api.js +++ b/ui/src/helpers/api.js @@ -111,8 +111,13 @@ export default class Api { }); } - getStats = async () => { + getPieStats = async () => { const response = await client.get("/status/accumulative"); return response.data; } + + getTimelineStats = async () => { + const response = await client.get("/status/accumulativeTiming"); + return response.data; + } }