mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-28 00:17:12 +01:00
feat(1-3267): use new API for chart creation (#9149)
Adds support for the new /traffic-search API behind a flag. When active, you'll be able to select month ranges as well as specific single months. Largely copies the existing network traffic component, and adds some minor tweaks to make it work with the new data. This is quite rough, but it gives us a base to build on for later. There's still things that we need to solve for in following PRs.
This commit is contained in:
parent
05e608ab09
commit
87a84426ec
@ -20,13 +20,17 @@ import {
|
|||||||
} from 'chart.js';
|
} from 'chart.js';
|
||||||
|
|
||||||
import { Bar } from 'react-chartjs-2';
|
import { Bar } from 'react-chartjs-2';
|
||||||
import { useInstanceTrafficMetrics } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
import {
|
||||||
|
useInstanceTrafficMetrics,
|
||||||
|
useInstanceTrafficMetrics2,
|
||||||
|
} from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
||||||
import type { Theme } from '@mui/material/styles/createTheme';
|
import type { Theme } from '@mui/material/styles/createTheme';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import { NetworkTrafficUsagePlanSummary } from './NetworkTrafficUsagePlanSummary';
|
import { NetworkTrafficUsagePlanSummary } from './NetworkTrafficUsagePlanSummary';
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
import annotationPlugin from 'chartjs-plugin-annotation';
|
||||||
import {
|
import {
|
||||||
type ChartDatasetType,
|
type ChartDatasetType,
|
||||||
|
newToChartData,
|
||||||
useTrafficDataEstimation,
|
useTrafficDataEstimation,
|
||||||
} from 'hooks/useTrafficData';
|
} from 'hooks/useTrafficData';
|
||||||
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
||||||
@ -36,6 +40,8 @@ import { BILLING_TRAFFIC_BUNDLE_PRICE } from 'component/admin/billing/BillingDas
|
|||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
import { PeriodSelector } from './PeriodSelector';
|
import { PeriodSelector } from './PeriodSelector';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { monthlyTrafficDataToCurrentUsage } from './monthly-traffic-data-to-current-usage';
|
||||||
|
|
||||||
const StyledBox = styled(Box)(({ theme }) => ({
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
@ -148,10 +154,173 @@ const NewHeader = styled('div')(() => ({
|
|||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const NetworkTrafficUsage: FC = () => {
|
const NewNetworkTrafficUsage: FC = () => {
|
||||||
|
usePageTitle('Network - Data Usage');
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const { isOss } = useUiConfig();
|
||||||
|
|
||||||
|
const { locationSettings } = useLocationSettings();
|
||||||
|
const {
|
||||||
|
record,
|
||||||
|
newPeriod,
|
||||||
|
setNewPeriod,
|
||||||
|
toTrafficUsageSum,
|
||||||
|
calculateOverageCost,
|
||||||
|
calculateEstimatedMonthlyCost,
|
||||||
|
} = useTrafficDataEstimation();
|
||||||
|
|
||||||
|
const includedTraffic = useTrafficLimit();
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
|
return createBarChartOptions(
|
||||||
|
theme,
|
||||||
|
(tooltipItems: any) => {
|
||||||
|
if (newPeriod.grouping === 'daily') {
|
||||||
|
const periodItem = record[newPeriod.month];
|
||||||
|
const tooltipDate = new Date(
|
||||||
|
periodItem.year,
|
||||||
|
periodItem.month,
|
||||||
|
Number.parseInt(tooltipItems[0].label),
|
||||||
|
);
|
||||||
|
return tooltipDate.toLocaleDateString(
|
||||||
|
locationSettings?.locale ?? 'en-US',
|
||||||
|
{
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new Date(tooltipItems[0].label).toLocaleDateString(
|
||||||
|
locationSettings?.locale ?? 'en-US',
|
||||||
|
{
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
includedTraffic,
|
||||||
|
);
|
||||||
|
}, [theme, newPeriod]);
|
||||||
|
|
||||||
|
const traffic = useInstanceTrafficMetrics2(newPeriod);
|
||||||
|
|
||||||
|
const data = newToChartData(traffic.usage);
|
||||||
|
|
||||||
|
const [usageTotal, setUsageTotal] = useState<number>(0);
|
||||||
|
|
||||||
|
const [overageCost, setOverageCost] = useState<number>(0);
|
||||||
|
|
||||||
|
const [estimatedMonthlyCost, setEstimatedMonthlyCost] = useState<number>(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
let usage: number;
|
||||||
|
if (newPeriod.grouping === 'monthly') {
|
||||||
|
usage = monthlyTrafficDataToCurrentUsage(traffic.usage);
|
||||||
|
} else {
|
||||||
|
usage = toTrafficUsageSum(data.datasets);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUsageTotal(usage);
|
||||||
|
if (includedTraffic > 0) {
|
||||||
|
const calculatedOverageCost = calculateOverageCost(
|
||||||
|
usage,
|
||||||
|
includedTraffic,
|
||||||
|
BILLING_TRAFFIC_BUNDLE_PRICE,
|
||||||
|
);
|
||||||
|
setOverageCost(calculatedOverageCost);
|
||||||
|
|
||||||
|
setEstimatedMonthlyCost(
|
||||||
|
calculateEstimatedMonthlyCost(
|
||||||
|
newPeriod.grouping === 'daily'
|
||||||
|
? newPeriod.month
|
||||||
|
: format(new Date(), 'yyyy-MM'),
|
||||||
|
data.datasets,
|
||||||
|
includedTraffic,
|
||||||
|
new Date(),
|
||||||
|
BILLING_TRAFFIC_BUNDLE_PRICE,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={isOss()}
|
||||||
|
show={<Alert severity='warning'>Not enabled.</Alert>}
|
||||||
|
elseShow={
|
||||||
|
<>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={includedTraffic > 0 && overageCost > 0}
|
||||||
|
show={
|
||||||
|
<Alert severity='warning' sx={{ mb: 4 }}>
|
||||||
|
<b>Heads up!</b> You are currently consuming
|
||||||
|
more requests than your plan includes and will
|
||||||
|
be billed according to our terms. Please see{' '}
|
||||||
|
<Link
|
||||||
|
component={RouterLink}
|
||||||
|
to='https://www.getunleash.io/pricing'
|
||||||
|
>
|
||||||
|
this page
|
||||||
|
</Link>{' '}
|
||||||
|
for more information. In order to reduce your
|
||||||
|
traffic consumption, you may configure an{' '}
|
||||||
|
<Link
|
||||||
|
component={RouterLink}
|
||||||
|
to='https://docs.getunleash.io/reference/unleash-edge'
|
||||||
|
>
|
||||||
|
Unleash Edge instance
|
||||||
|
</Link>{' '}
|
||||||
|
in your own datacenter.
|
||||||
|
</Alert>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<StyledBox>
|
||||||
|
<NewHeader>
|
||||||
|
{
|
||||||
|
// todo: add new usage plan summary that works for monthly data as well as daily
|
||||||
|
}
|
||||||
|
|
||||||
|
<NetworkTrafficUsagePlanSummary
|
||||||
|
usageTotal={usageTotal}
|
||||||
|
includedTraffic={includedTraffic}
|
||||||
|
overageCost={overageCost}
|
||||||
|
estimatedMonthlyCost={estimatedMonthlyCost}
|
||||||
|
/>
|
||||||
|
<PeriodSelector
|
||||||
|
selectedPeriod={newPeriod}
|
||||||
|
setPeriod={setNewPeriod}
|
||||||
|
/>
|
||||||
|
</NewHeader>
|
||||||
|
<Bar
|
||||||
|
data={data}
|
||||||
|
plugins={[customHighlightPlugin()]} // todo: accomodate wide bars when grouping by month
|
||||||
|
options={options}
|
||||||
|
aria-label='An instance metrics line chart with two lines: requests per second for admin API and requests per second for client API' // todo: this isn't correct at all!
|
||||||
|
/>
|
||||||
|
</StyledBox>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NetworkTrafficUsage: FC = () => {
|
||||||
|
const useNewNetworkTraffic = useUiFlag('dataUsageMultiMonthView');
|
||||||
|
return useNewNetworkTraffic ? (
|
||||||
|
<NewNetworkTrafficUsage />
|
||||||
|
) : (
|
||||||
|
<OldNetworkTrafficUsage />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const OldNetworkTrafficUsage: FC = () => {
|
||||||
usePageTitle('Network - Data Usage');
|
usePageTitle('Network - Data Usage');
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const showMultiMonthSelector = useUiFlag('dataUsageMultiMonthView');
|
|
||||||
|
|
||||||
const { isOss } = useUiConfig();
|
const { isOss } = useUiConfig();
|
||||||
|
|
||||||
@ -279,29 +448,13 @@ export const NetworkTrafficUsage: FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<StyledBox>
|
<StyledBox>
|
||||||
{showMultiMonthSelector ? (
|
|
||||||
<NewHeader>
|
|
||||||
<NetworkTrafficUsagePlanSummary
|
|
||||||
usageTotal={usageTotal}
|
|
||||||
includedTraffic={includedTraffic}
|
|
||||||
overageCost={overageCost}
|
|
||||||
estimatedMonthlyCost={estimatedMonthlyCost}
|
|
||||||
/>
|
|
||||||
<PeriodSelector
|
|
||||||
selectedPeriod={period}
|
|
||||||
setPeriod={setPeriod}
|
|
||||||
/>
|
|
||||||
</NewHeader>
|
|
||||||
) : (
|
|
||||||
<Grid container component='header' spacing={2}>
|
<Grid container component='header' spacing={2}>
|
||||||
<Grid item xs={12} md={10}>
|
<Grid item xs={12} md={10}>
|
||||||
<NetworkTrafficUsagePlanSummary
|
<NetworkTrafficUsagePlanSummary
|
||||||
usageTotal={usageTotal}
|
usageTotal={usageTotal}
|
||||||
includedTraffic={includedTraffic}
|
includedTraffic={includedTraffic}
|
||||||
overageCost={overageCost}
|
overageCost={overageCost}
|
||||||
estimatedMonthlyCost={
|
estimatedMonthlyCost={estimatedMonthlyCost}
|
||||||
estimatedMonthlyCost
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={2}>
|
<Grid item xs={12} md={2}>
|
||||||
@ -310,9 +463,7 @@ export const NetworkTrafficUsage: FC = () => {
|
|||||||
name='dataperiod'
|
name='dataperiod'
|
||||||
options={selectablePeriods}
|
options={selectablePeriods}
|
||||||
value={period}
|
value={period}
|
||||||
onChange={(e) =>
|
onChange={(e) => setPeriod(e.target.value)}
|
||||||
setPeriod(e.target.value)
|
|
||||||
}
|
|
||||||
style={{
|
style={{
|
||||||
minWidth: '100%',
|
minWidth: '100%',
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(2),
|
||||||
@ -321,7 +472,7 @@ export const NetworkTrafficUsage: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
|
||||||
<Grid item xs={12} md={2}>
|
<Grid item xs={12} md={2}>
|
||||||
<Bar
|
<Bar
|
||||||
data={data}
|
data={data}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { type FC, useState } from 'react';
|
import type { ChartDataSelection } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
export type Period = {
|
export type Period = {
|
||||||
key: string;
|
key: string;
|
||||||
@ -147,25 +148,13 @@ type Selection =
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
selectedPeriod: string;
|
selectedPeriod: ChartDataSelection;
|
||||||
setPeriod: (period: string) => void;
|
setPeriod: (period: ChartDataSelection) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
|
export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
|
||||||
const selectablePeriods = getSelectablePeriods();
|
const selectablePeriods = getSelectablePeriods();
|
||||||
|
|
||||||
// this is for dev purposes; only to show how the design will work when you select a range.
|
|
||||||
const [tempOverride, setTempOverride] = useState<Selection | null>();
|
|
||||||
|
|
||||||
const select = (value: Selection) => {
|
|
||||||
if (value.type === 'month') {
|
|
||||||
setTempOverride(null);
|
|
||||||
setPeriod(value.value);
|
|
||||||
} else {
|
|
||||||
setTempOverride(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rangeOptions = [3, 6, 12].map((monthsBack) => ({
|
const rangeOptions = [3, 6, 12].map((monthsBack) => ({
|
||||||
value: monthsBack,
|
value: monthsBack,
|
||||||
label: `Last ${monthsBack} months`,
|
label: `Last ${monthsBack} months`,
|
||||||
@ -183,17 +172,17 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
|
|||||||
<li key={period.label}>
|
<li key={period.label}>
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
!tempOverride &&
|
selectedPeriod.grouping === 'daily' &&
|
||||||
period.key === selectedPeriod
|
period.key === selectedPeriod.month
|
||||||
? 'selected'
|
? 'selected'
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
type='button'
|
type='button'
|
||||||
disabled={!period.selectable}
|
disabled={!period.selectable}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
select({
|
setPeriod({
|
||||||
type: 'month',
|
grouping: 'daily',
|
||||||
value: period.key,
|
month: period.key,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -211,16 +200,15 @@ export const PeriodSelector: FC<Props> = ({ selectedPeriod, setPeriod }) => {
|
|||||||
<li key={option.label}>
|
<li key={option.label}>
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
tempOverride &&
|
selectedPeriod.grouping === 'monthly' &&
|
||||||
tempOverride.type === 'range' &&
|
option.value === selectedPeriod.monthsBack
|
||||||
option.value === tempOverride.monthsBack
|
|
||||||
? 'selected'
|
? 'selected'
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
select({
|
setPeriod({
|
||||||
type: 'range',
|
grouping: 'monthly',
|
||||||
monthsBack: option.value,
|
monthsBack: option.value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
import { format } from 'date-fns';
|
||||||
|
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
|
||||||
|
|
||||||
|
export const monthlyTrafficDataToCurrentUsage = (
|
||||||
|
usage?: TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
) => {
|
||||||
|
if (!usage) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const currentMonth = format(new Date(), 'yyyy-MM');
|
||||||
|
return usage.apiData.reduce((acc, current) => {
|
||||||
|
const currentPoint = current.dataPoints.find(
|
||||||
|
({ period }) => period === currentMonth,
|
||||||
|
);
|
||||||
|
const pointUsage =
|
||||||
|
currentPoint?.trafficTypes.reduce(
|
||||||
|
(acc, next) => acc + next.count,
|
||||||
|
0,
|
||||||
|
) ?? 0;
|
||||||
|
return acc + pointUsage;
|
||||||
|
}, 0);
|
||||||
|
};
|
@ -2,7 +2,12 @@ import useSWR from 'swr';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import type { TrafficUsageDataSegmentedSchema } from 'openapi';
|
import type {
|
||||||
|
TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
TrafficUsageDataSegmentedCombinedSchemaApiDataItem,
|
||||||
|
TrafficUsageDataSegmentedSchema,
|
||||||
|
} from 'openapi';
|
||||||
|
import { endOfMonth, format, startOfMonth, subMonths } from 'date-fns';
|
||||||
|
|
||||||
export interface IInstanceTrafficMetricsResponse {
|
export interface IInstanceTrafficMetricsResponse {
|
||||||
usage: TrafficUsageDataSegmentedSchema;
|
usage: TrafficUsageDataSegmentedSchema;
|
||||||
@ -33,6 +38,64 @@ export const useInstanceTrafficMetrics = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChartDataSelection =
|
||||||
|
| {
|
||||||
|
grouping: 'daily';
|
||||||
|
month: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
grouping: 'monthly';
|
||||||
|
monthsBack: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fromSelection = (selection: ChartDataSelection) => {
|
||||||
|
const fmt = (date: Date) => format(date, 'yyyy-MM-dd');
|
||||||
|
if (selection.grouping === 'daily') {
|
||||||
|
const month = new Date(selection.month);
|
||||||
|
const from = fmt(month);
|
||||||
|
const to = fmt(endOfMonth(month));
|
||||||
|
return { from, to };
|
||||||
|
} else {
|
||||||
|
const now = new Date();
|
||||||
|
const from = fmt(startOfMonth(subMonths(now, selection.monthsBack)));
|
||||||
|
const to = fmt(endOfMonth(now));
|
||||||
|
return { from, to };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SegmentedSchemaApiData =
|
||||||
|
TrafficUsageDataSegmentedCombinedSchemaApiDataItem;
|
||||||
|
|
||||||
|
export type InstanceTrafficMetricsResponse2 = {
|
||||||
|
usage: TrafficUsageDataSegmentedCombinedSchema;
|
||||||
|
|
||||||
|
refetch: () => void;
|
||||||
|
|
||||||
|
loading: boolean;
|
||||||
|
|
||||||
|
error?: Error;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useInstanceTrafficMetrics2 = (
|
||||||
|
selection: ChartDataSelection,
|
||||||
|
): InstanceTrafficMetricsResponse2 => {
|
||||||
|
const { from, to } = fromSelection(selection);
|
||||||
|
|
||||||
|
const apiPath = `api/admin/metrics/traffic-search?grouping=${selection.grouping}&from=${from}&to=${to}`;
|
||||||
|
|
||||||
|
const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
usage: data,
|
||||||
|
loading: !error && !data,
|
||||||
|
refetch: () => mutate(),
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
[data, error, mutate],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const fetcher = (path: string) => {
|
const fetcher = (path: string) => {
|
||||||
return fetch(path)
|
return fetch(path)
|
||||||
.then(handleErrorResponses('Instance Metrics'))
|
.then(handleErrorResponses('Instance Metrics'))
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import type { IInstanceTrafficMetricsResponse } from './api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
import type {
|
||||||
|
ChartDataSelection,
|
||||||
|
IInstanceTrafficMetricsResponse,
|
||||||
|
SegmentedSchemaApiData,
|
||||||
|
} from './api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
||||||
import type { ChartDataset } from 'chart.js';
|
import type { ChartDataset } from 'chart.js';
|
||||||
|
import {
|
||||||
|
addDays,
|
||||||
|
addMonths,
|
||||||
|
differenceInCalendarDays,
|
||||||
|
differenceInCalendarMonths,
|
||||||
|
format,
|
||||||
|
} from 'date-fns';
|
||||||
|
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
|
||||||
|
|
||||||
const DEFAULT_TRAFFIC_DATA_UNIT_COST = 5;
|
const DEFAULT_TRAFFIC_DATA_UNIT_COST = 5;
|
||||||
const DEFAULT_TRAFFIC_DATA_UNIT_SIZE = 1_000_000;
|
const DEFAULT_TRAFFIC_DATA_UNIT_SIZE = 1_000_000;
|
||||||
@ -104,6 +116,110 @@ const toPeriodsRecord = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const newToChartData = (
|
||||||
|
traffic?: TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
): { datasets: ChartDatasetType[]; labels: (string | number)[] } => {
|
||||||
|
if (!traffic) {
|
||||||
|
return { labels: [], datasets: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traffic.grouping === 'monthly') {
|
||||||
|
return toMonthlyChartData(traffic);
|
||||||
|
} else {
|
||||||
|
return toDailyChartData(traffic);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const prepareApiData = (
|
||||||
|
apiData: TrafficUsageDataSegmentedCombinedSchema['apiData'],
|
||||||
|
) =>
|
||||||
|
apiData
|
||||||
|
.filter((item) => item.apiPath in endpointsInfo)
|
||||||
|
.sort(
|
||||||
|
(item1: SegmentedSchemaApiData, item2: SegmentedSchemaApiData) =>
|
||||||
|
endpointsInfo[item1.apiPath].order -
|
||||||
|
endpointsInfo[item2.apiPath].order,
|
||||||
|
);
|
||||||
|
|
||||||
|
const toMonthlyChartData = (
|
||||||
|
traffic: TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
): { datasets: ChartDatasetType[]; labels: string[] } => {
|
||||||
|
const from = new Date(traffic.dateRange.from);
|
||||||
|
const to = new Date(traffic.dateRange.to);
|
||||||
|
const numMonths = Math.abs(differenceInCalendarMonths(to, from)) + 1;
|
||||||
|
const formatMonth = (date: Date) => format(date, 'yyyy-MM');
|
||||||
|
|
||||||
|
const datasets = prepareApiData(traffic.apiData).map(
|
||||||
|
(item: SegmentedSchemaApiData) => {
|
||||||
|
const monthsRec: { [month: string]: number } = {};
|
||||||
|
for (let i = 0; i < numMonths; i++) {
|
||||||
|
monthsRec[formatMonth(addMonths(from, i))] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const month of Object.values(item.dataPoints)) {
|
||||||
|
monthsRec[month.period] = month.trafficTypes[0].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const epInfo = endpointsInfo[item.apiPath];
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: epInfo.label,
|
||||||
|
data: Object.values(monthsRec),
|
||||||
|
backgroundColor: epInfo.color,
|
||||||
|
hoverBackgroundColor: epInfo.color,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const labels = Array.from({ length: numMonths }).map((_, index) =>
|
||||||
|
formatMonth(addMonths(from, index)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return { datasets, labels };
|
||||||
|
};
|
||||||
|
|
||||||
|
const toDailyChartData = (
|
||||||
|
traffic: TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
): { datasets: ChartDatasetType[]; labels: number[] } => {
|
||||||
|
const from = new Date(traffic.dateRange.from);
|
||||||
|
const to = new Date(traffic.dateRange.to);
|
||||||
|
const numDays = Math.abs(differenceInCalendarDays(to, from)) + 1;
|
||||||
|
const formatDay = (date: Date) => format(date, 'yyyy-MM-dd');
|
||||||
|
|
||||||
|
const daysRec: { [day: string]: number } = {};
|
||||||
|
for (let i = 0; i < numDays; i++) {
|
||||||
|
daysRec[formatDay(addDays(from, i))] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDaysRec = () => ({
|
||||||
|
...daysRec,
|
||||||
|
});
|
||||||
|
|
||||||
|
const datasets = prepareApiData(traffic.apiData).map(
|
||||||
|
(item: SegmentedSchemaApiData) => {
|
||||||
|
const daysRec = getDaysRec();
|
||||||
|
|
||||||
|
for (const day of Object.values(item.dataPoints)) {
|
||||||
|
daysRec[day.period] = day.trafficTypes[0].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const epInfo = endpointsInfo[item.apiPath];
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: epInfo.label,
|
||||||
|
data: Object.values(daysRec),
|
||||||
|
backgroundColor: epInfo.color,
|
||||||
|
hoverBackgroundColor: epInfo.color,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// simplification: assuming days run in a single month from the 1st onwards
|
||||||
|
const labels = Array.from({ length: numDays }).map((_, index) => index + 1);
|
||||||
|
|
||||||
|
return { datasets, labels };
|
||||||
|
};
|
||||||
|
|
||||||
const toChartData = (
|
const toChartData = (
|
||||||
days: number[],
|
days: number[],
|
||||||
traffic: IInstanceTrafficMetricsResponse,
|
traffic: IInstanceTrafficMetricsResponse,
|
||||||
@ -236,11 +352,18 @@ export const useTrafficDataEstimation = () => {
|
|||||||
const record = toPeriodsRecord(selectablePeriods);
|
const record = toPeriodsRecord(selectablePeriods);
|
||||||
const [period, setPeriod] = useState<string>(selectablePeriods[0].key);
|
const [period, setPeriod] = useState<string>(selectablePeriods[0].key);
|
||||||
|
|
||||||
|
const [newPeriod, setNewPeriod] = useState<ChartDataSelection>({
|
||||||
|
grouping: 'daily',
|
||||||
|
month: selectablePeriods[0].key,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
calculateTrafficDataCost,
|
calculateTrafficDataCost,
|
||||||
record,
|
record,
|
||||||
period,
|
period,
|
||||||
setPeriod,
|
setPeriod,
|
||||||
|
newPeriod,
|
||||||
|
setNewPeriod,
|
||||||
selectablePeriods,
|
selectablePeriods,
|
||||||
getDayLabels,
|
getDayLabels,
|
||||||
currentPeriod,
|
currentPeriod,
|
||||||
|
Loading…
Reference in New Issue
Block a user