mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-23 00:16:25 +01:00
chore: remove dataUsageMultiMonthView flag (#9429)
Remove data usage multi month view flag and deprecated components and functions.
This commit is contained in:
parent
2e086161eb
commit
8629cda4d7
@ -1,26 +1,13 @@
|
|||||||
import { endOfMonth, format, startOfMonth } from 'date-fns';
|
import { endOfMonth, format, startOfMonth } from 'date-fns';
|
||||||
import {
|
import { useTrafficSearch } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
||||||
useInstanceTrafficMetrics,
|
|
||||||
useTrafficSearch,
|
|
||||||
} from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
calculateOverageCost,
|
calculateOverageCost,
|
||||||
calculateTotalUsage,
|
calculateTotalUsage,
|
||||||
} from 'utils/traffic-calculations';
|
} from 'utils/traffic-calculations';
|
||||||
import { BILLING_TRAFFIC_BUNDLE_PRICE } from './BillingPlan';
|
import { BILLING_TRAFFIC_BUNDLE_PRICE } from './BillingPlan';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
import { useTrafficDataEstimation } from 'hooks/useTrafficData';
|
|
||||||
|
|
||||||
export const useOverageCost = (includedTraffic: number) => {
|
export const useOverageCost = (includedTraffic: number) => {
|
||||||
if (useUiFlag('dataUsageMultiMonthView')) {
|
|
||||||
return useNewOverageCostCalculation(includedTraffic);
|
|
||||||
} else {
|
|
||||||
return useOldOverageCostCalculation(includedTraffic);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const useNewOverageCostCalculation = (includedTraffic: number) => {
|
|
||||||
if (!includedTraffic) {
|
if (!includedTraffic) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -46,34 +33,3 @@ const useNewOverageCostCalculation = (includedTraffic: number) => {
|
|||||||
|
|
||||||
return overageCost;
|
return overageCost;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useOldOverageCostCalculation = (includedTraffic: number) => {
|
|
||||||
const {
|
|
||||||
currentPeriod,
|
|
||||||
toChartData,
|
|
||||||
toTrafficUsageSum,
|
|
||||||
endpointsInfo,
|
|
||||||
getDayLabels,
|
|
||||||
} = useTrafficDataEstimation();
|
|
||||||
|
|
||||||
const traffic = useInstanceTrafficMetrics(currentPeriod.key);
|
|
||||||
|
|
||||||
const overageCost = useMemo(() => {
|
|
||||||
if (!includedTraffic) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const trafficData = toChartData(
|
|
||||||
getDayLabels(currentPeriod.dayCount),
|
|
||||||
traffic,
|
|
||||||
endpointsInfo,
|
|
||||||
);
|
|
||||||
const totalTraffic = toTrafficUsageSum(trafficData);
|
|
||||||
return calculateOverageCost(
|
|
||||||
totalTraffic,
|
|
||||||
includedTraffic,
|
|
||||||
BILLING_TRAFFIC_BUNDLE_PRICE,
|
|
||||||
);
|
|
||||||
}, [includedTraffic, traffic, currentPeriod, endpointsInfo]);
|
|
||||||
|
|
||||||
return overageCost;
|
|
||||||
};
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { type FC, useEffect, useMemo, useState } from 'react';
|
import type { FC } from 'react';
|
||||||
import useTheme from '@mui/material/styles/useTheme';
|
|
||||||
import styled from '@mui/material/styles/styled';
|
import styled from '@mui/material/styles/styled';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import Select from 'component/common/select';
|
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import { Alert, Link } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import {
|
import {
|
||||||
@ -18,25 +16,14 @@ 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 Grid from '@mui/material/Grid';
|
|
||||||
import { NetworkTrafficUsagePlanSummary } from './NetworkTrafficUsagePlanSummary';
|
|
||||||
import annotationPlugin from 'chartjs-plugin-annotation';
|
import annotationPlugin from 'chartjs-plugin-annotation';
|
||||||
import {
|
|
||||||
calculateEstimatedMonthlyCost as deprecatedCalculateEstimatedMonthlyCost,
|
|
||||||
useTrafficDataEstimation,
|
|
||||||
} from 'hooks/useTrafficData';
|
|
||||||
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
||||||
import { useTrafficLimit } from './hooks/useTrafficLimit';
|
import { useTrafficLimit } from './hooks/useTrafficLimit';
|
||||||
import { BILLING_TRAFFIC_BUNDLE_PRICE } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
|
|
||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
|
||||||
import { PeriodSelector } from './PeriodSelector';
|
import { PeriodSelector } from './PeriodSelector';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { OverageInfo, RequestSummary } from './RequestSummary';
|
import { OverageInfo, RequestSummary } from './RequestSummary';
|
||||||
import { calculateOverageCost } from 'utils/traffic-calculations';
|
|
||||||
import { currentMonth } from './dates';
|
import { currentMonth } from './dates';
|
||||||
import { type ChartDatasetType, getChartLabel } from './chart-functions';
|
import { getChartLabel } from './chart-functions';
|
||||||
import { createBarChartOptions } from './bar-chart-options';
|
|
||||||
import { useTrafficStats } from './hooks/useStats';
|
import { useTrafficStats } from './hooks/useStats';
|
||||||
import { BoldText, StyledBox, TopRow } from './SharedComponents';
|
import { BoldText, StyledBox, TopRow } from './SharedComponents';
|
||||||
import { useChartDataSelection } from './hooks/useChartDataSelection';
|
import { useChartDataSelection } from './hooks/useChartDataSelection';
|
||||||
@ -48,7 +35,7 @@ const TrafficInfoBoxes = styled('div')(({ theme }) => ({
|
|||||||
gap: theme.spacing(2, 4),
|
gap: theme.spacing(2, 4),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const NewNetworkTrafficUsage: FC = () => {
|
const NetworkTrafficUsage: FC = () => {
|
||||||
usePageTitle('Network - Data Usage');
|
usePageTitle('Network - Data Usage');
|
||||||
|
|
||||||
const estimateTrafficDataCost = useUiFlag('estimateTrafficDataCost');
|
const estimateTrafficDataCost = useUiFlag('estimateTrafficDataCost');
|
||||||
@ -142,183 +129,6 @@ const NewNetworkTrafficUsage: FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NetworkTrafficUsage: FC = () => {
|
|
||||||
const useNewNetworkTraffic = useUiFlag('dataUsageMultiMonthView');
|
|
||||||
return useNewNetworkTraffic ? (
|
|
||||||
<NewNetworkTrafficUsage />
|
|
||||||
) : (
|
|
||||||
<OldNetworkTrafficUsage />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const OldNetworkTrafficUsage: FC = () => {
|
|
||||||
usePageTitle('Network - Data Usage');
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const { isOss } = useUiConfig();
|
|
||||||
|
|
||||||
const { locationSettings } = useLocationSettings();
|
|
||||||
const {
|
|
||||||
record,
|
|
||||||
period,
|
|
||||||
setPeriod,
|
|
||||||
selectablePeriods,
|
|
||||||
getDayLabels,
|
|
||||||
toChartData,
|
|
||||||
toTrafficUsageSum,
|
|
||||||
endpointsInfo,
|
|
||||||
} = useTrafficDataEstimation();
|
|
||||||
|
|
||||||
const includedTraffic = useTrafficLimit();
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return createBarChartOptions(
|
|
||||||
theme,
|
|
||||||
(tooltipItems: any) => {
|
|
||||||
const periodItem = record[period];
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
includedTraffic,
|
|
||||||
);
|
|
||||||
}, [theme, period]);
|
|
||||||
|
|
||||||
const traffic = useInstanceTrafficMetrics(period);
|
|
||||||
|
|
||||||
const [labels, setLabels] = useState<number[]>([]);
|
|
||||||
|
|
||||||
const [datasets, setDatasets] = useState<ChartDatasetType[]>([]);
|
|
||||||
|
|
||||||
const [usageTotal, setUsageTotal] = useState<number>(0);
|
|
||||||
|
|
||||||
const [overageCost, setOverageCost] = useState<number>(0);
|
|
||||||
|
|
||||||
const [estimatedMonthlyCost, setEstimatedMonthlyCost] = useState<number>(0);
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
labels,
|
|
||||||
datasets,
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setDatasets(toChartData(labels, traffic, endpointsInfo));
|
|
||||||
}, [labels, traffic]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (record && period) {
|
|
||||||
const periodData = record[period];
|
|
||||||
setLabels(getDayLabels(periodData.dayCount));
|
|
||||||
}
|
|
||||||
}, [period]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
const usage = toTrafficUsageSum(data.datasets);
|
|
||||||
setUsageTotal(usage);
|
|
||||||
if (includedTraffic > 0) {
|
|
||||||
const calculatedOverageCost = calculateOverageCost(
|
|
||||||
usage,
|
|
||||||
includedTraffic,
|
|
||||||
BILLING_TRAFFIC_BUNDLE_PRICE,
|
|
||||||
);
|
|
||||||
setOverageCost(calculatedOverageCost);
|
|
||||||
|
|
||||||
setEstimatedMonthlyCost(
|
|
||||||
deprecatedCalculateEstimatedMonthlyCost(
|
|
||||||
period,
|
|
||||||
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>
|
|
||||||
<Grid container component='header' spacing={2}>
|
|
||||||
<Grid item xs={12} md={10}>
|
|
||||||
<NetworkTrafficUsagePlanSummary
|
|
||||||
usageTotal={usageTotal}
|
|
||||||
includedTraffic={includedTraffic}
|
|
||||||
overageCost={overageCost}
|
|
||||||
estimatedMonthlyCost={estimatedMonthlyCost}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={2}>
|
|
||||||
<Select
|
|
||||||
id='dataperiod-select'
|
|
||||||
name='dataperiod'
|
|
||||||
options={selectablePeriods}
|
|
||||||
value={period}
|
|
||||||
onChange={(e) => setPeriod(e.target.value)}
|
|
||||||
style={{
|
|
||||||
minWidth: '100%',
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
}}
|
|
||||||
formControlStyles={{ width: '100%' }}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12} md={2}>
|
|
||||||
<Bar
|
|
||||||
data={data}
|
|
||||||
plugins={[customHighlightPlugin()]}
|
|
||||||
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'
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</StyledBox>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register dependencies that we need to draw the chart.
|
// Register dependencies that we need to draw the chart.
|
||||||
ChartJS.register(
|
ChartJS.register(
|
||||||
annotationPlugin,
|
annotationPlugin,
|
||||||
|
@ -2,42 +2,10 @@ 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 {
|
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
|
||||||
TrafficUsageDataSegmentedCombinedSchema,
|
|
||||||
TrafficUsageDataSegmentedSchema,
|
|
||||||
} from 'openapi';
|
|
||||||
import { cleanTrafficData } from 'utils/traffic-calculations';
|
import { cleanTrafficData } from 'utils/traffic-calculations';
|
||||||
|
|
||||||
export interface IInstanceTrafficMetricsResponse {
|
export type InstanceTrafficMetricsResponse = {
|
||||||
usage: TrafficUsageDataSegmentedSchema;
|
|
||||||
|
|
||||||
refetch: () => void;
|
|
||||||
|
|
||||||
loading: boolean;
|
|
||||||
|
|
||||||
error?: Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useInstanceTrafficMetrics = (
|
|
||||||
period: string,
|
|
||||||
): IInstanceTrafficMetricsResponse => {
|
|
||||||
const { data, error, mutate } = useSWR(
|
|
||||||
formatApiPath(`api/admin/metrics/traffic/${period}`),
|
|
||||||
fetcher,
|
|
||||||
);
|
|
||||||
|
|
||||||
return useMemo(
|
|
||||||
() => ({
|
|
||||||
usage: data,
|
|
||||||
loading: !error && !data,
|
|
||||||
refetch: () => mutate(),
|
|
||||||
error,
|
|
||||||
}),
|
|
||||||
[data, error, mutate],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InstanceTrafficMetricsResponse2 = {
|
|
||||||
refetch: () => void;
|
refetch: () => void;
|
||||||
result:
|
result:
|
||||||
| { state: 'success'; data: TrafficUsageDataSegmentedCombinedSchema }
|
| { state: 'success'; data: TrafficUsageDataSegmentedCombinedSchema }
|
||||||
@ -54,7 +22,7 @@ export const useTrafficSearch = (
|
|||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
},
|
},
|
||||||
): InstanceTrafficMetricsResponse2 => {
|
): InstanceTrafficMetricsResponse => {
|
||||||
const apiPath = `api/admin/metrics/traffic-search?grouping=${grouping}&from=${from}&to=${to}`;
|
const apiPath = `api/admin/metrics/traffic-search?grouping=${grouping}&from=${from}&to=${to}`;
|
||||||
|
|
||||||
const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher);
|
const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher);
|
||||||
|
@ -1,228 +0,0 @@
|
|||||||
import type { ChartDatasetType } from 'component/admin/network/NetworkTrafficUsage/chart-functions';
|
|
||||||
import type { IInstanceTrafficMetricsResponse } from './api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import {
|
|
||||||
DEFAULT_TRAFFIC_DATA_UNIT_COST,
|
|
||||||
DEFAULT_TRAFFIC_DATA_UNIT_SIZE,
|
|
||||||
calculateOverageCost,
|
|
||||||
} from 'utils/traffic-calculations';
|
|
||||||
import {
|
|
||||||
currentMonth,
|
|
||||||
daysInCurrentMonth,
|
|
||||||
} from 'component/admin/network/NetworkTrafficUsage/dates';
|
|
||||||
|
|
||||||
export type SelectablePeriod = {
|
|
||||||
key: string;
|
|
||||||
dayCount: number;
|
|
||||||
label: string;
|
|
||||||
year: number;
|
|
||||||
month: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EndpointInfo = {
|
|
||||||
label: string;
|
|
||||||
color: string;
|
|
||||||
order: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const endpointsInfo: Record<string, EndpointInfo> = {
|
|
||||||
'/api/admin': {
|
|
||||||
label: 'Admin',
|
|
||||||
color: '#6D66D9',
|
|
||||||
order: 1,
|
|
||||||
},
|
|
||||||
'/api/frontend': {
|
|
||||||
label: 'Frontend',
|
|
||||||
color: '#A39EFF',
|
|
||||||
order: 2,
|
|
||||||
},
|
|
||||||
'/api/client': {
|
|
||||||
label: 'Server',
|
|
||||||
color: '#D8D6FF',
|
|
||||||
order: 3,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const padMonth = (month: number): string => month.toString().padStart(2, '0');
|
|
||||||
|
|
||||||
export const toSelectablePeriod = (
|
|
||||||
date: Date,
|
|
||||||
label?: string,
|
|
||||||
): SelectablePeriod => {
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = date.getMonth();
|
|
||||||
const period = `${year}-${padMonth(month + 1)}`;
|
|
||||||
const dayCount = new Date(year, month + 1, 0).getDate();
|
|
||||||
return {
|
|
||||||
key: period,
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
dayCount,
|
|
||||||
label:
|
|
||||||
label ||
|
|
||||||
date.toLocaleString('en-US', { month: 'long', year: 'numeric' }),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentDate = new Date(Date.now());
|
|
||||||
const currentPeriod = toSelectablePeriod(currentDate, 'Current month');
|
|
||||||
|
|
||||||
const getSelectablePeriods = (): SelectablePeriod[] => {
|
|
||||||
const selectablePeriods = [currentPeriod];
|
|
||||||
for (
|
|
||||||
let subtractMonthCount = 1;
|
|
||||||
subtractMonthCount < 13;
|
|
||||||
subtractMonthCount++
|
|
||||||
) {
|
|
||||||
// JavaScript wraps around the year, so we don't need to handle that.
|
|
||||||
const date = new Date(
|
|
||||||
currentDate.getFullYear(),
|
|
||||||
currentDate.getMonth() - subtractMonthCount,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
if (date > new Date('2024-03-31')) {
|
|
||||||
selectablePeriods.push(toSelectablePeriod(date));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selectablePeriods;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toPeriodsRecord = (
|
|
||||||
periods: SelectablePeriod[],
|
|
||||||
): Record<string, SelectablePeriod> => {
|
|
||||||
return periods.reduce(
|
|
||||||
(acc, period) => {
|
|
||||||
acc[period.key] = period;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, SelectablePeriod>,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const toChartData = (
|
|
||||||
days: number[],
|
|
||||||
traffic: IInstanceTrafficMetricsResponse,
|
|
||||||
endpointsInfo: Record<string, EndpointInfo>,
|
|
||||||
): ChartDatasetType[] => {
|
|
||||||
if (!traffic || !traffic.usage || !traffic.usage.apiData) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = traffic.usage.apiData
|
|
||||||
.filter((item) => !!endpointsInfo[item.apiPath])
|
|
||||||
.sort(
|
|
||||||
(item1: any, item2: any) =>
|
|
||||||
endpointsInfo[item1.apiPath].order -
|
|
||||||
endpointsInfo[item2.apiPath].order,
|
|
||||||
)
|
|
||||||
.map((item: any) => {
|
|
||||||
const daysRec = days.reduce(
|
|
||||||
(acc, day: number) => {
|
|
||||||
acc[`d${day}`] = 0;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, number>,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const dayKey in item.days) {
|
|
||||||
const day = item.days[dayKey];
|
|
||||||
const dayNum = new Date(Date.parse(day.day)).getUTCDate();
|
|
||||||
daysRec[`d${dayNum}`] = day.trafficTypes[0].count;
|
|
||||||
}
|
|
||||||
const epInfo = endpointsInfo[item.apiPath];
|
|
||||||
|
|
||||||
return {
|
|
||||||
label: epInfo.label,
|
|
||||||
data: Object.values(daysRec),
|
|
||||||
backgroundColor: epInfo.color,
|
|
||||||
hoverBackgroundColor: epInfo.color,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toTrafficUsageSum = (trafficData: ChartDatasetType[]): number => {
|
|
||||||
const data = trafficData.reduce(
|
|
||||||
(acc: number, current: ChartDatasetType) => {
|
|
||||||
return (
|
|
||||||
acc +
|
|
||||||
current.data.reduce(
|
|
||||||
(acc_inner, current_inner) => acc_inner + current_inner,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const calculateProjectedUsage = (
|
|
||||||
today: number,
|
|
||||||
trafficData: ChartDatasetType[],
|
|
||||||
daysInPeriod: number,
|
|
||||||
) => {
|
|
||||||
if (today < 5) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const spliceToYesterday = today - 1;
|
|
||||||
const trafficDataUpToYesterday = trafficData.map((item) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
data: item.data.slice(0, spliceToYesterday),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const dataUsage = toTrafficUsageSum(trafficDataUpToYesterday);
|
|
||||||
return (dataUsage / spliceToYesterday) * daysInPeriod;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const calculateEstimatedMonthlyCost = (
|
|
||||||
period: string,
|
|
||||||
trafficData: ChartDatasetType[],
|
|
||||||
includedTraffic: number,
|
|
||||||
currentDate: Date,
|
|
||||||
trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST,
|
|
||||||
trafficUnitSize = DEFAULT_TRAFFIC_DATA_UNIT_SIZE,
|
|
||||||
) => {
|
|
||||||
if (period !== currentMonth) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const today = currentDate.getDate();
|
|
||||||
const projectedUsage = calculateProjectedUsage(
|
|
||||||
today,
|
|
||||||
trafficData,
|
|
||||||
daysInCurrentMonth,
|
|
||||||
);
|
|
||||||
|
|
||||||
return calculateOverageCost(
|
|
||||||
projectedUsage,
|
|
||||||
includedTraffic,
|
|
||||||
trafficUnitCost,
|
|
||||||
trafficUnitSize,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDayLabels = (dayCount: number): number[] => {
|
|
||||||
return [...Array(dayCount).keys()].map((i) => i + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useTrafficDataEstimation = () => {
|
|
||||||
const selectablePeriods = getSelectablePeriods();
|
|
||||||
const record = toPeriodsRecord(selectablePeriods);
|
|
||||||
const [period, setPeriod] = useState<string>(selectablePeriods[0].key);
|
|
||||||
|
|
||||||
return {
|
|
||||||
record,
|
|
||||||
period,
|
|
||||||
setPeriod,
|
|
||||||
selectablePeriods,
|
|
||||||
getDayLabels,
|
|
||||||
currentPeriod,
|
|
||||||
toChartData,
|
|
||||||
toTrafficUsageSum,
|
|
||||||
endpointsInfo,
|
|
||||||
};
|
|
||||||
};
|
|
@ -90,7 +90,6 @@ export type UiFlags = {
|
|||||||
showUserDeviceCount?: boolean;
|
showUserDeviceCount?: boolean;
|
||||||
flagOverviewRedesign?: boolean;
|
flagOverviewRedesign?: boolean;
|
||||||
granularAdminPermissions?: boolean;
|
granularAdminPermissions?: boolean;
|
||||||
dataUsageMultiMonthView?: boolean;
|
|
||||||
consumptionModel?: boolean;
|
consumptionModel?: boolean;
|
||||||
edgeObservability?: boolean;
|
edgeObservability?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -11,10 +11,6 @@ import type {
|
|||||||
TrafficUsageDataSegmentedCombinedSchema,
|
TrafficUsageDataSegmentedCombinedSchema,
|
||||||
TrafficUsageDataSegmentedCombinedSchemaApiDataItem,
|
TrafficUsageDataSegmentedCombinedSchemaApiDataItem,
|
||||||
} from 'openapi';
|
} from 'openapi';
|
||||||
import {
|
|
||||||
calculateEstimatedMonthlyCost as deprecatedCalculateEstimatedMonthlyCost,
|
|
||||||
calculateProjectedUsage as deprecatedCalculateProjectedUsage,
|
|
||||||
} from 'hooks/useTrafficData';
|
|
||||||
|
|
||||||
const testData4Days = [
|
const testData4Days = [
|
||||||
{
|
{
|
||||||
@ -97,21 +93,13 @@ describe('traffic overage calculation', () => {
|
|||||||
const period = toSelectablePeriod(now);
|
const period = toSelectablePeriod(now);
|
||||||
const testNow = new Date(now.getFullYear(), now.getMonth(), 4);
|
const testNow = new Date(now.getFullYear(), now.getMonth(), 4);
|
||||||
const includedTraffic = 53_000_000;
|
const includedTraffic = 53_000_000;
|
||||||
const result = deprecatedCalculateEstimatedMonthlyCost(
|
|
||||||
period.key,
|
|
||||||
testData4Days,
|
|
||||||
includedTraffic,
|
|
||||||
testNow,
|
|
||||||
);
|
|
||||||
expect(result).toBe(0);
|
|
||||||
|
|
||||||
const rawData = trafficData4Days(now);
|
const rawData = trafficData4Days(now);
|
||||||
const result2 = calculateEstimatedMonthlyCost(
|
const result = calculateEstimatedMonthlyCost(
|
||||||
rawData,
|
rawData,
|
||||||
includedTraffic,
|
includedTraffic,
|
||||||
testNow,
|
testNow,
|
||||||
);
|
);
|
||||||
expect(result2).toBe(result);
|
expect(result).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('needs 5 days or more to estimate for the month', () => {
|
it('needs 5 days or more to estimate for the month', () => {
|
||||||
@ -120,27 +108,18 @@ describe('traffic overage calculation', () => {
|
|||||||
testData[1].data.push(23_000_000);
|
testData[1].data.push(23_000_000);
|
||||||
testData[2].data.push(23_000_000);
|
testData[2].data.push(23_000_000);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const period = toSelectablePeriod(now);
|
|
||||||
const testNow = new Date(now.getFullYear(), now.getMonth(), 5);
|
const testNow = new Date(now.getFullYear(), now.getMonth(), 5);
|
||||||
const includedTraffic = 53_000_000;
|
const includedTraffic = 53_000_000;
|
||||||
const result = deprecatedCalculateEstimatedMonthlyCost(
|
|
||||||
period.key,
|
|
||||||
testData,
|
|
||||||
includedTraffic,
|
|
||||||
testNow,
|
|
||||||
);
|
|
||||||
expect(result).toBeGreaterThan(1430);
|
|
||||||
|
|
||||||
const rawData = trafficData4Days(now);
|
const rawData = trafficData4Days(now);
|
||||||
rawData[0].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
rawData[0].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
||||||
rawData[1].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
rawData[1].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
||||||
rawData[2].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
rawData[2].dataPoints.push(dataPoint(now)(5, 23_000_000));
|
||||||
const result2 = calculateEstimatedMonthlyCost(
|
const result = calculateEstimatedMonthlyCost(
|
||||||
rawData,
|
rawData,
|
||||||
includedTraffic,
|
includedTraffic,
|
||||||
testNow,
|
testNow,
|
||||||
);
|
);
|
||||||
expect(result2).toBe(result);
|
expect(result).toBeGreaterThan(1430);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('estimates projected data usage', () => {
|
it('estimates projected data usage', () => {
|
||||||
@ -151,24 +130,17 @@ describe('traffic overage calculation', () => {
|
|||||||
// Testing April 5th of 2024 (30 days)
|
// Testing April 5th of 2024 (30 days)
|
||||||
const now = new Date(2024, 3, 5);
|
const now = new Date(2024, 3, 5);
|
||||||
const period = toSelectablePeriod(now);
|
const period = toSelectablePeriod(now);
|
||||||
const result = deprecatedCalculateProjectedUsage(
|
|
||||||
now.getDate(),
|
|
||||||
testData,
|
|
||||||
period.dayCount,
|
|
||||||
);
|
|
||||||
// 22_500_000 * 3 * 30 = 2_025_000_000
|
|
||||||
expect(result).toBe(2_025_000_000);
|
|
||||||
|
|
||||||
const rawData = trafficData4Days(now);
|
const rawData = trafficData4Days(now);
|
||||||
rawData[0].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
rawData[0].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
rawData[1].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
rawData[1].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
rawData[2].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
rawData[2].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
const result2 = calculateProjectedUsage({
|
const result = calculateProjectedUsage({
|
||||||
dayOfMonth: now.getDate(),
|
dayOfMonth: now.getDate(),
|
||||||
daysInMonth: period.dayCount,
|
daysInMonth: period.dayCount,
|
||||||
trafficData: rawData,
|
trafficData: rawData,
|
||||||
});
|
});
|
||||||
expect(result2).toBe(result);
|
// 22_500_000 * 3 * 30 = 2_025_000_000
|
||||||
|
expect(result).toBe(2_025_000_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports custom price and unit size', () => {
|
it('supports custom price and unit size', () => {
|
||||||
@ -194,9 +166,13 @@ describe('traffic overage calculation', () => {
|
|||||||
const includedTraffic = 53_000_000;
|
const includedTraffic = 53_000_000;
|
||||||
const trafficUnitSize = 500_000;
|
const trafficUnitSize = 500_000;
|
||||||
const trafficUnitCost = 10;
|
const trafficUnitCost = 10;
|
||||||
const result = deprecatedCalculateEstimatedMonthlyCost(
|
|
||||||
period.key,
|
const rawData = trafficData4Days(now);
|
||||||
testData,
|
rawData[0].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
|
rawData[1].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
|
rawData[2].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
||||||
|
const result = calculateEstimatedMonthlyCost(
|
||||||
|
rawData,
|
||||||
includedTraffic,
|
includedTraffic,
|
||||||
testNow,
|
testNow,
|
||||||
trafficUnitCost,
|
trafficUnitCost,
|
||||||
@ -208,20 +184,6 @@ describe('traffic overage calculation', () => {
|
|||||||
const overageUnits = Math.floor(overage / trafficUnitSize);
|
const overageUnits = Math.floor(overage / trafficUnitSize);
|
||||||
const total = overageUnits * trafficUnitCost;
|
const total = overageUnits * trafficUnitCost;
|
||||||
expect(result).toBe(total);
|
expect(result).toBe(total);
|
||||||
|
|
||||||
const rawData = trafficData4Days(now);
|
|
||||||
rawData[0].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
|
||||||
rawData[1].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
|
||||||
rawData[2].dataPoints.push(dataPoint(now)(5, 22_500_000));
|
|
||||||
const result2 = calculateEstimatedMonthlyCost(
|
|
||||||
rawData,
|
|
||||||
includedTraffic,
|
|
||||||
testNow,
|
|
||||||
trafficUnitCost,
|
|
||||||
trafficUnitSize,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result2).toBe(result);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,9 +24,6 @@ export interface ITrafficDataUsageStore
|
|||||||
extends Store<IStatTrafficUsage, IStatTrafficUsageKey> {
|
extends Store<IStatTrafficUsage, IStatTrafficUsageKey> {
|
||||||
upsert(trafficDataUsage: IStatTrafficUsage): Promise<void>;
|
upsert(trafficDataUsage: IStatTrafficUsage): Promise<void>;
|
||||||
getTrafficDataUsageForPeriod(period: string): Promise<IStatTrafficUsage[]>;
|
getTrafficDataUsageForPeriod(period: string): Promise<IStatTrafficUsage[]>;
|
||||||
getTrafficDataForMonthRange(
|
|
||||||
monthsBack: number,
|
|
||||||
): Promise<IStatMonthlyTrafficUsage[]>;
|
|
||||||
getDailyTrafficDataUsageForPeriod(
|
getDailyTrafficDataUsageForPeriod(
|
||||||
from: Date,
|
from: Date,
|
||||||
to: Date,
|
to: Date,
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { differenceInCalendarMonths, subMonths } from 'date-fns';
|
import {
|
||||||
|
differenceInCalendarMonths,
|
||||||
|
endOfMonth,
|
||||||
|
startOfMonth,
|
||||||
|
subMonths,
|
||||||
|
} from 'date-fns';
|
||||||
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
|
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
|
||||||
import getLogger from '../../../test/fixtures/no-logger';
|
import getLogger from '../../../test/fixtures/no-logger';
|
||||||
import type { ITrafficDataUsageStore, IUnleashStores } from '../../types';
|
import type { ITrafficDataUsageStore, IUnleashStores } from '../../types';
|
||||||
@ -234,8 +239,13 @@ test('can query for monthly aggregation of data for a specified range', async ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const monthsBack of [3, 6, 12]) {
|
for (const monthsBack of [3, 6, 12]) {
|
||||||
|
const to = endOfMonth(now);
|
||||||
|
const from = subMonths(startOfMonth(now), monthsBack);
|
||||||
const result =
|
const result =
|
||||||
await trafficDataUsageStore.getTrafficDataForMonthRange(monthsBack);
|
await trafficDataUsageStore.getMonthlyTrafficDataUsageForPeriod(
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
);
|
||||||
|
|
||||||
// should have the current month and the preceding n months (one entry per group)
|
// should have the current month and the preceding n months (one entry per group)
|
||||||
expect(result.length).toBe((monthsBack + 1) * 2);
|
expect(result.length).toBe((monthsBack + 1) * 2);
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { endOfDay, endOfMonth, startOfDay, startOfMonth } from 'date-fns';
|
||||||
endOfDay,
|
|
||||||
endOfMonth,
|
|
||||||
startOfDay,
|
|
||||||
startOfMonth,
|
|
||||||
subMonths,
|
|
||||||
} from 'date-fns';
|
|
||||||
import type { Db } from '../../db/db';
|
import type { Db } from '../../db/db';
|
||||||
import type { Logger, LogProvider } from '../../logger';
|
import type { Logger, LogProvider } from '../../logger';
|
||||||
import type {
|
import type {
|
||||||
@ -148,13 +142,4 @@ export class TrafficDataUsageStore implements ITrafficDataUsageStore {
|
|||||||
endOfMonth(month),
|
endOfMonth(month),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @deprecated: remove with flag `dataUsageMultiMonthView`
|
|
||||||
async getTrafficDataForMonthRange(
|
|
||||||
monthsBack: number,
|
|
||||||
): Promise<IStatMonthlyTrafficUsage[]> {
|
|
||||||
const to = endOfMonth(new Date());
|
|
||||||
const from = startOfMonth(subMonths(to, monthsBack));
|
|
||||||
return this.getMonthlyTrafficDataUsageForPeriod(from, to);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ export type IFlagKey =
|
|||||||
| 'etagVariant'
|
| 'etagVariant'
|
||||||
| 'deltaApi'
|
| 'deltaApi'
|
||||||
| 'uniqueSdkTracking'
|
| 'uniqueSdkTracking'
|
||||||
| 'dataUsageMultiMonthView'
|
|
||||||
| 'consumptionModel'
|
| 'consumptionModel'
|
||||||
| 'teamsIntegrationChangeRequests'
|
| 'teamsIntegrationChangeRequests'
|
||||||
| 'edgeObservability'
|
| 'edgeObservability'
|
||||||
@ -299,10 +298,6 @@ const flags: IFlags = {
|
|||||||
process.env.UNLEASH_EXPERIMENTAL_UNIQUE_SDK_TRACKING,
|
process.env.UNLEASH_EXPERIMENTAL_UNIQUE_SDK_TRACKING,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
dataUsageMultiMonthView: parseEnvVarBoolean(
|
|
||||||
process.env.UNLEASH_EXPERIMENTAL_DATA_USAGE_MULTI_MONTH_VIEW,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
consumptionModel: parseEnvVarBoolean(
|
consumptionModel: parseEnvVarBoolean(
|
||||||
process.env.EXPERIMENTAL_CONSUMPTION_MODEL,
|
process.env.EXPERIMENTAL_CONSUMPTION_MODEL,
|
||||||
false,
|
false,
|
||||||
|
@ -56,7 +56,6 @@ process.nextTick(async () => {
|
|||||||
granularAdminPermissions: true,
|
granularAdminPermissions: true,
|
||||||
deltaApi: true,
|
deltaApi: true,
|
||||||
uniqueSdkTracking: true,
|
uniqueSdkTracking: true,
|
||||||
dataUsageMultiMonthView: true,
|
|
||||||
filterExistingFlagNames: true,
|
filterExistingFlagNames: true,
|
||||||
teamsIntegrationChangeRequests: true,
|
teamsIntegrationChangeRequests: true,
|
||||||
simplifyDisableFeature: true,
|
simplifyDisableFeature: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user