mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-09 11:14:29 +02:00
Fixes a number of issues that would surface in UTC-n (where n > 1) timezones. I've not found a way to check this with tests (and it looks like [we weren't able to last time either](https://github.com/Unleash/unleash/pull/9110/files#r1919746328)), so all the testing's been done manually by adjusting my system time and zone. (From what I understand, you can't generate a Date with a specific TZ offset in JS: it's only utc or local time) Resolved: - [x] Selecting "Jan" in the dropdown results in the selection being "December" (off by one in the selector) - [x] Selecting a month view only gives you one data point (and it's probably empty). Wrong date parsing on the way out resulted in sending `{ from: "2025-02-28", to: "2025-02-28"}` instead of `{ from: "2025-03-01", to: "2025-03-31"}` - [x] The dates we create when making "daysRec" need to be adjusted. They showed the wrong month, so the dates were off. - [x] Make sure the labels are correct when hovering over. Again: we used the wrong month for generating these. - [x] The available months are wrong. Incorrect month parsing again. - [x] The request summary month is wrong. You guessed it: incorrect month parsing
158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
import type {
|
|
TrafficUsageDataSegmentedCombinedSchema,
|
|
TrafficUsageDataSegmentedCombinedSchemaApiDataItem,
|
|
} from 'openapi';
|
|
import { getDaysInMonth } from 'date-fns';
|
|
import { format } from 'date-fns';
|
|
import { parseDateString } from 'component/admin/network/NetworkTrafficUsage/dates';
|
|
export const DEFAULT_TRAFFIC_DATA_UNIT_COST = 5;
|
|
export const DEFAULT_TRAFFIC_DATA_UNIT_SIZE = 1_000_000;
|
|
|
|
export const TRAFFIC_MEASUREMENT_START_DATE = parseDateString('2024-05-01');
|
|
|
|
export const METERED_TRAFFIC_ENDPOINTS = [
|
|
'/api/admin',
|
|
'/api/frontend',
|
|
'/api/client',
|
|
];
|
|
|
|
export const cleanTrafficData = (
|
|
data: TrafficUsageDataSegmentedCombinedSchema,
|
|
): TrafficUsageDataSegmentedCombinedSchema => {
|
|
const { apiData, ...rest } = data;
|
|
const cleanedApiData = apiData
|
|
.filter((item) => METERED_TRAFFIC_ENDPOINTS.includes(item.apiPath))
|
|
.map((item) => {
|
|
item.dataPoints = item.dataPoints.filter(
|
|
({ period }) =>
|
|
new Date(period) >= TRAFFIC_MEASUREMENT_START_DATE,
|
|
);
|
|
return item;
|
|
});
|
|
return { apiData: cleanedApiData, ...rest };
|
|
};
|
|
|
|
const monthlyTrafficDataToCurrentUsage = (
|
|
apiData: TrafficUsageDataSegmentedCombinedSchemaApiDataItem[],
|
|
latestMonth: string,
|
|
) => {
|
|
return apiData.reduce((acc, current) => {
|
|
const currentPoint = current.dataPoints.find(
|
|
({ period }) => period === latestMonth,
|
|
);
|
|
const pointUsage =
|
|
currentPoint?.trafficTypes.reduce(
|
|
(acc, next) => acc + next.count,
|
|
0,
|
|
) ?? 0;
|
|
return acc + pointUsage;
|
|
}, 0);
|
|
};
|
|
|
|
const dailyTrafficDataToCurrentUsage = (
|
|
apiData: TrafficUsageDataSegmentedCombinedSchemaApiDataItem[],
|
|
) => {
|
|
return apiData
|
|
.flatMap((endpoint) =>
|
|
endpoint.dataPoints.flatMap((dataPoint) =>
|
|
dataPoint.trafficTypes.map((trafficType) => trafficType.count),
|
|
),
|
|
)
|
|
.reduce((acc, count) => acc + count, 0);
|
|
};
|
|
|
|
// 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
|
|
export const calculateTotalUsage = (
|
|
data: TrafficUsageDataSegmentedCombinedSchema,
|
|
): number => {
|
|
const { grouping, apiData } = data;
|
|
if (grouping === 'monthly') {
|
|
const latestMonth = format(new Date(data.dateRange.to), 'yyyy-MM');
|
|
return monthlyTrafficDataToCurrentUsage(apiData, latestMonth);
|
|
} else {
|
|
return dailyTrafficDataToCurrentUsage(apiData);
|
|
}
|
|
};
|
|
|
|
const calculateTrafficDataCost = (
|
|
trafficData: number,
|
|
trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST,
|
|
trafficUnitSize = DEFAULT_TRAFFIC_DATA_UNIT_SIZE,
|
|
) => {
|
|
const unitCount = Math.ceil(trafficData / trafficUnitSize);
|
|
return unitCount * trafficUnitCost;
|
|
};
|
|
|
|
export const calculateOverageCost = (
|
|
dataUsage: number,
|
|
includedTraffic: number,
|
|
trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST,
|
|
trafficUnitSize = DEFAULT_TRAFFIC_DATA_UNIT_SIZE,
|
|
): number => {
|
|
if (dataUsage === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const overage =
|
|
Math.floor((dataUsage - includedTraffic) / trafficUnitSize) *
|
|
trafficUnitSize;
|
|
return overage > 0
|
|
? calculateTrafficDataCost(overage, trafficUnitCost, trafficUnitSize)
|
|
: 0;
|
|
};
|
|
|
|
export const calculateProjectedUsage = ({
|
|
dayOfMonth,
|
|
daysInMonth,
|
|
trafficData,
|
|
}: {
|
|
dayOfMonth: number;
|
|
daysInMonth: number;
|
|
trafficData: TrafficUsageDataSegmentedCombinedSchemaApiDataItem[];
|
|
}) => {
|
|
if (dayOfMonth < 5) {
|
|
return 0;
|
|
}
|
|
|
|
const trafficDataUpToYesterday = trafficData.map((item) => {
|
|
return {
|
|
...item,
|
|
dataPoints: item.dataPoints.filter(
|
|
(point) => Number(point.period.slice(-2)) < dayOfMonth,
|
|
),
|
|
};
|
|
});
|
|
|
|
const dataUsage = dailyTrafficDataToCurrentUsage(trafficDataUpToYesterday);
|
|
|
|
return (dataUsage / (dayOfMonth - 1)) * daysInMonth;
|
|
};
|
|
|
|
export const calculateEstimatedMonthlyCost = (
|
|
trafficData: TrafficUsageDataSegmentedCombinedSchemaApiDataItem[],
|
|
includedTraffic: number,
|
|
currentDate: Date,
|
|
trafficUnitCost = DEFAULT_TRAFFIC_DATA_UNIT_COST,
|
|
trafficUnitSize = DEFAULT_TRAFFIC_DATA_UNIT_SIZE,
|
|
) => {
|
|
if (!trafficData) {
|
|
return 0;
|
|
}
|
|
const dayOfMonth = currentDate.getDate();
|
|
const daysInMonth = getDaysInMonth(currentDate);
|
|
|
|
const projectedUsage = calculateProjectedUsage({
|
|
dayOfMonth,
|
|
daysInMonth,
|
|
trafficData,
|
|
});
|
|
|
|
return calculateOverageCost(
|
|
projectedUsage,
|
|
includedTraffic,
|
|
trafficUnitCost,
|
|
trafficUnitSize,
|
|
);
|
|
};
|