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:
parent
a11c965bec
commit
90e5adb695
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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)}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -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(
|
||||||
|
@ -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) => {
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user