1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-23 13:46:45 +02:00

chore: use union type for traffic search data (#9221)

Makes the data returned from the traffic search a union type to avoid
nasty object-is-undefined errors at runtime.

It requires more explicit handling, sure, but it means we don't need
to accept undefined.
This commit is contained in:
Thomas Heartman 2025-02-05 11:50:39 +01:00 committed by GitHub
parent a11c965bec
commit 90e5adb695
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 91 additions and 80 deletions

View File

@ -30,16 +30,19 @@ const useNewOverageCostCalculation = (includedTraffic: number) => {
const from = formatDate(startOfMonth(now)); const from = formatDate(startOfMonth(now));
const to = formatDate(endOfMonth(now)); const to = formatDate(endOfMonth(now));
const { usage } = useTrafficSearch('daily', { from, to }); const { result } = useTrafficSearch('daily', { from, to });
const overageCost = useMemo(() => { const overageCost = useMemo(() => {
const totalUsage = calculateTotalUsage(usage); if (result.state !== 'success') {
return 0;
}
const totalUsage = calculateTotalUsage(result.data);
return calculateOverageCost( return calculateOverageCost(
totalUsage, totalUsage,
includedTraffic, includedTraffic,
BILLING_TRAFFIC_BUNDLE_PRICE, BILLING_TRAFFIC_BUNDLE_PRICE,
); );
}, [includedTraffic, usage]); }, [includedTraffic, JSON.stringify(result)]);
return overageCost; return overageCost;
}; };

View File

@ -178,6 +178,62 @@ const BoldText = styled('span')(({ theme }) => ({
fontWeight: 'bold', fontWeight: 'bold',
})); }));
const useTrafficStats = (
includedTraffic: number,
chartDataSelection: ChartDataSelection,
) => {
const { result } = useTrafficSearch(
chartDataSelection.grouping,
toDateRange(chartDataSelection, currentDate),
);
const results = useMemo(() => {
if (result.state !== 'success') {
return {
chartData: { datasets: [], labels: [] },
usageTotal: 0,
overageCost: 0,
estimatedMonthlyCost: 0,
requestSummaryUsage: 0,
};
}
const traffic = result.data;
const chartData = newToChartData(traffic);
const usageTotal = calculateTotalUsage(traffic);
const overageCost = calculateOverageCost(
usageTotal,
includedTraffic,
BILLING_TRAFFIC_BUNDLE_PRICE,
);
const estimatedMonthlyCost = calculateEstimatedMonthlyCost(
traffic.apiData,
includedTraffic,
currentDate,
BILLING_TRAFFIC_BUNDLE_PRICE,
);
const requestSummaryUsage =
chartDataSelection.grouping === 'daily'
? usageTotal
: averageTrafficPreviousMonths(traffic);
return {
chartData,
usageTotal,
overageCost,
estimatedMonthlyCost,
requestSummaryUsage,
};
}, [
JSON.stringify(result),
includedTraffic,
JSON.stringify(chartDataSelection),
]);
return results;
};
const NewNetworkTrafficUsage: FC = () => { const NewNetworkTrafficUsage: FC = () => {
usePageTitle('Network - Data Usage'); usePageTitle('Network - Data Usage');
const theme = useTheme(); const theme = useTheme();
@ -233,25 +289,13 @@ const NewNetworkTrafficUsage: FC = () => {
); );
}, [theme, chartDataSelection]); }, [theme, chartDataSelection]);
const traffic = useTrafficSearch( const {
chartDataSelection.grouping, chartData,
toDateRange(chartDataSelection, currentDate),
);
const data = newToChartData(traffic.usage);
const usageTotal = calculateTotalUsage(traffic.usage);
const overageCost = calculateOverageCost(
usageTotal, usageTotal,
includedTraffic, overageCost,
BILLING_TRAFFIC_BUNDLE_PRICE, estimatedMonthlyCost,
); requestSummaryUsage,
} = useTrafficStats(includedTraffic, chartDataSelection);
const estimatedMonthlyCost = calculateEstimatedMonthlyCost(
traffic.usage?.apiData,
includedTraffic,
currentDate,
BILLING_TRAFFIC_BUNDLE_PRICE,
);
const showOverageCalculations = const showOverageCalculations =
chartDataSelection.grouping === 'daily' && chartDataSelection.grouping === 'daily' &&
@ -296,13 +340,7 @@ const NewNetworkTrafficUsage: FC = () => {
<TrafficInfoBoxes> <TrafficInfoBoxes>
<RequestSummary <RequestSummary
period={chartDataSelection} period={chartDataSelection}
usageTotal={ usageTotal={requestSummaryUsage}
chartDataSelection.grouping === 'daily'
? usageTotal
: averageTrafficPreviousMonths(
traffic.usage,
)
}
includedTraffic={includedTraffic} includedTraffic={includedTraffic}
/> />
{showOverageCalculations && ( {showOverageCalculations && (
@ -321,7 +359,7 @@ const NewNetworkTrafficUsage: FC = () => {
/> />
</TopRow> </TopRow>
<Bar <Bar
data={data} data={chartData}
plugins={[customHighlightPlugin()]} plugins={[customHighlightPlugin()]}
options={options} options={options}
aria-label={getChartLabel(chartDataSelection)} aria-label={getChartLabel(chartDataSelection)}

View File

@ -5,7 +5,7 @@ import { currentMonth } from './dates';
export const averageTrafficPreviousMonths = ( export const averageTrafficPreviousMonths = (
traffic: TrafficUsageDataSegmentedCombinedSchema, traffic: TrafficUsageDataSegmentedCombinedSchema,
) => { ) => {
if (!traffic || traffic.grouping === 'daily') { if (traffic.grouping === 'daily') {
return 0; return 0;
} }

View File

@ -148,11 +148,4 @@ describe('toChartData', () => {
expect(toChartData(input)).toMatchObject(expectedOutput); expect(toChartData(input)).toMatchObject(expectedOutput);
}); });
test('returns empty data if traffic is undefined', () => {
expect(toChartData(undefined)).toStrictEqual({
labels: [],
datasets: [],
});
});
}); });

View File

@ -12,12 +12,8 @@ import type { ChartDataSelection } from './chart-data-selection';
export type ChartDatasetType = ChartDataset<'bar'>; export type ChartDatasetType = ChartDataset<'bar'>;
export const toChartData = ( export const toChartData = (
traffic?: TrafficUsageDataSegmentedCombinedSchema, traffic: TrafficUsageDataSegmentedCombinedSchema,
): { datasets: ChartDatasetType[]; labels: string[] } => { ): { datasets: ChartDatasetType[]; labels: string[] } => {
if (!traffic) {
return { labels: [], datasets: [] };
}
const { newRecord, labels } = getLabelsAndRecords(traffic); const { newRecord, labels } = getLabelsAndRecords(traffic);
const datasets = traffic.apiData const datasets = traffic.apiData
.sort( .sort(

View File

@ -38,13 +38,11 @@ export const useInstanceTrafficMetrics = (
}; };
export type InstanceTrafficMetricsResponse2 = { export type InstanceTrafficMetricsResponse2 = {
usage: TrafficUsageDataSegmentedCombinedSchema;
refetch: () => void; refetch: () => void;
result:
loading: boolean; | { state: 'success'; data: TrafficUsageDataSegmentedCombinedSchema }
| { state: 'error'; error: Error }
error?: Error; | { state: 'loading' };
}; };
export const useTrafficSearch = ( export const useTrafficSearch = (
@ -61,15 +59,17 @@ export const useTrafficSearch = (
const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher); const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher);
return useMemo( return useMemo(() => {
() => ({ const result = data
usage: cleanTrafficData(data) as any, ? { state: 'success' as const, data: cleanTrafficData(data) }
loading: !error && !data, : error
? { state: 'error' as const, error }
: { state: 'loading' as const };
return {
refetch: () => mutate(), refetch: () => mutate(),
error, result,
}), };
[data, error, mutate], }, [data, error, mutate]);
);
}; };
const fetcher = (path: string) => { const fetcher = (path: string) => {

View File

@ -171,16 +171,6 @@ describe('traffic overage calculation', () => {
expect(result2).toBe(result); expect(result2).toBe(result);
}); });
it("doesn't die if traffic is undefined", () => {
expect(
calculateEstimatedMonthlyCost(
undefined,
500_000,
new Date('2024-05-15'),
),
).toBe(0);
});
it('supports custom price and unit size', () => { it('supports custom price and unit size', () => {
const dataUsage = 54_000_000; const dataUsage = 54_000_000;
const includedTraffic = 53_000_000; const includedTraffic = 53_000_000;

View File

@ -16,12 +16,8 @@ export const METERED_TRAFFIC_ENDPOINTS = [
]; ];
export const cleanTrafficData = ( export const cleanTrafficData = (
data?: TrafficUsageDataSegmentedCombinedSchema, data: TrafficUsageDataSegmentedCombinedSchema,
): TrafficUsageDataSegmentedCombinedSchema | undefined => { ): TrafficUsageDataSegmentedCombinedSchema => {
if (!data) {
return;
}
const { apiData, ...rest } = data; const { apiData, ...rest } = data;
const cleanedApiData = apiData const cleanedApiData = apiData
.filter((item) => METERED_TRAFFIC_ENDPOINTS.includes(item.apiPath)) .filter((item) => METERED_TRAFFIC_ENDPOINTS.includes(item.apiPath))
@ -67,11 +63,8 @@ const dailyTrafficDataToCurrentUsage = (
// Return the total number of requests for the selected month if showing daily // Return the total number of requests for the selected month if showing daily
// data, or the total for the most recent month if showing monthly data // data, or the total for the most recent month if showing monthly data
export const calculateTotalUsage = ( export const calculateTotalUsage = (
data?: TrafficUsageDataSegmentedCombinedSchema, data: TrafficUsageDataSegmentedCombinedSchema,
): number => { ): number => {
if (!data) {
return 0;
}
const { grouping, apiData } = data; const { grouping, apiData } = data;
if (grouping === 'monthly') { if (grouping === 'monthly') {
const latestMonth = format(new Date(data.dateRange.to), 'yyyy-MM'); const latestMonth = format(new Date(data.dateRange.to), 'yyyy-MM');
@ -135,9 +128,7 @@ export const calculateProjectedUsage = ({
}; };
export const calculateEstimatedMonthlyCost = ( export const calculateEstimatedMonthlyCost = (
trafficData: trafficData: TrafficUsageDataSegmentedCombinedSchemaApiDataItem[],
| TrafficUsageDataSegmentedCombinedSchemaApiDataItem[]
| undefined,
includedTraffic: number, includedTraffic: number,
currentDate: Date, currentDate: Date,
trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST, trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST,