1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

feat: read backend connections UI (#9526)

This commit is contained in:
Mateusz Kwasniewski 2025-03-13 09:56:29 +01:00 committed by GitHub
parent 22f51df76c
commit 1b7f91cd4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 100 additions and 69 deletions

View File

@ -21,6 +21,7 @@ import {
import annotationPlugin from 'chartjs-plugin-annotation';
import { useChartDataSelection } from './hooks/useChartDataSelection';
// TODO: consider renaming to Connection Consumption
export const BackendConnections: FC = () => {
usePageTitle('Network - Backend Connections');

View File

@ -1,4 +1,7 @@
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
import type {
MeteredConnectionsSchema,
TrafficUsageDataSegmentedCombinedSchema,
} from 'openapi';
import {
toConnectionChartData,
toTrafficUsageChartData,
@ -152,9 +155,9 @@ describe('toTrafficUsageChartData', () => {
});
describe('toConnectionChartData', () => {
const dataPoint = (period: string, count: number) => ({
const dataPoint = (period: string, connections: number) => ({
period,
trafficTypes: [{ count, group: 'successful-requests' }],
connections,
});
const fromEndpointInfo = (endpoint: keyof typeof endpointsInfo) => {
@ -167,7 +170,7 @@ describe('toConnectionChartData', () => {
};
test('monthly data conversion', () => {
const input: TrafficUsageDataSegmentedCombinedSchema = {
const input: MeteredConnectionsSchema = {
grouping: 'monthly',
dateRange: {
from: '2025-01-01',
@ -175,16 +178,12 @@ describe('toConnectionChartData', () => {
},
apiData: [
{
apiPath: '/api/admin', // filter out
dataPoints: [dataPoint('2025-06', 5)],
},
{
apiPath: '/api/client',
meteredGroup: 'default',
dataPoints: [
dataPoint('2025-06', 10 * 5 * 60 * 24 * 30),
dataPoint('2025-01', 7 * 5 * 60 * 24 * 31),
dataPoint('2025-03', 11 * 5 * 60 * 24 * 31),
dataPoint('2025-04', 13 * 5 * 60 * 24 * 30),
dataPoint('2025-06', 10),
dataPoint('2025-01', 7),
dataPoint('2025-03', 11),
dataPoint('2025-04', 13),
],
},
],
@ -194,7 +193,9 @@ describe('toConnectionChartData', () => {
datasets: [
{
data: [7, 0, 11, 13, 0, 10],
...fromEndpointInfo('/api/client'),
hoverBackgroundColor: '#6D66D9',
label: 'Connections',
backgroundColor: '#6D66D9',
},
],
labels: [
@ -211,7 +212,7 @@ describe('toConnectionChartData', () => {
});
test('daily data conversion', () => {
const input: TrafficUsageDataSegmentedCombinedSchema = {
const input: MeteredConnectionsSchema = {
grouping: 'daily',
dateRange: {
from: '2025-01-01',
@ -219,16 +220,12 @@ describe('toConnectionChartData', () => {
},
apiData: [
{
apiPath: '/api/admin', // filter out
dataPoints: [dataPoint('2025-01-01', 5)],
},
{
apiPath: '/api/client',
meteredGroup: 'default',
dataPoints: [
dataPoint('2025-01-02', 2 * 5 * 60 * 24),
dataPoint('2025-01-17', 6 * 5 * 60 * 24),
dataPoint('2025-01-19', 4 * 5 * 60 * 24),
dataPoint('2025-01-06', 8 * 5 * 60 * 24),
dataPoint('2025-01-02', 2),
dataPoint('2025-01-17', 6),
dataPoint('2025-01-19', 4),
dataPoint('2025-01-06', 8),
],
},
],
@ -241,7 +238,9 @@ describe('toConnectionChartData', () => {
0, 2, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
...fromEndpointInfo('/api/client'),
hoverBackgroundColor: '#6D66D9',
label: 'Connections',
backgroundColor: '#6D66D9',
},
],
labels: Array.from({ length: 31 }).map((_, index) =>

View File

@ -1,13 +1,14 @@
import type { ChartDataset } from 'chart.js';
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
import type {
MeteredConnectionsSchema,
TrafficUsageDataSegmentedCombinedSchema,
} from 'openapi';
import { endpointsInfo } from './endpoint-info';
import {
addDays,
addMonths,
differenceInCalendarDays,
differenceInCalendarMonths,
getDaysInMonth,
parseISO,
} from 'date-fns';
import { formatDay, formatMonth } from './dates';
import type { ChartDataSelection } from './chart-data-selection';
@ -45,51 +46,38 @@ export const toTrafficUsageChartData = (
};
export const toConnectionChartData = (
traffic: TrafficUsageDataSegmentedCombinedSchema,
traffic: MeteredConnectionsSchema,
): { datasets: ChartDatasetType[]; labels: string[] } => {
const { newRecord, labels } = getLabelsAndRecords(traffic);
const datasets = traffic.apiData
.filter((apiData) => apiData.apiPath === '/api/client')
.sort(
(item1, item2) =>
endpointsInfo[item1.apiPath].order -
endpointsInfo[item2.apiPath].order,
)
.map((item) => {
const record = newRecord();
for (const dataPoint of Object.values(item.dataPoints)) {
const date = parseISO(dataPoint.period);
const requestCount = dataPoint.trafficTypes[0].count;
const datasets = traffic.apiData.map((item) => {
const record = newRecord();
for (const dataPoint of Object.values(item.dataPoints)) {
const requestCount = dataPoint.connections;
record[dataPoint.period] = requestCount;
}
if (traffic.grouping === 'monthly') {
// 1 connections = 7200 * days in month requests per day
const daysInMonth = getDaysInMonth(date);
record[dataPoint.period] = Number(
(requestCount / (daysInMonth * 7200)).toFixed(1),
);
} else {
// 1 connection = 7200 requests per day
record[dataPoint.period] = Number(
(requestCount / 7200).toFixed(1),
);
}
}
const epInfo = {
label: 'Connections',
color: '#6D66D9',
order: 1,
};
const epInfo = endpointsInfo[item.apiPath];
return {
label: epInfo.label,
data: Object.values(record),
backgroundColor: epInfo.color,
hoverBackgroundColor: epInfo.color,
};
});
return {
label: epInfo.label,
data: Object.values(record),
backgroundColor: epInfo.color,
hoverBackgroundColor: epInfo.color,
};
});
return { datasets, labels };
};
const getLabelsAndRecords = (
traffic: TrafficUsageDataSegmentedCombinedSchema,
traffic: Pick<
TrafficUsageDataSegmentedCombinedSchema,
'dateRange' | 'grouping'
>,
) => {
if (traffic.grouping === 'monthly') {
const from = new Date(traffic.dateRange.from);

View File

@ -13,6 +13,7 @@ import {
} from 'utils/traffic-calculations';
import { BILLING_TRAFFIC_BUNDLE_PRICE } from '../../../billing/BillingDashboard/BillingPlan/BillingPlan';
import { averageTrafficPreviousMonths } from '../average-traffic-previous-months';
import { useConnectionsConsumption } from 'hooks/api/getters/useConnectionsConsumption/useConnectionsConsumption';
export const useTrafficStats = (
includedTraffic: number,
@ -72,7 +73,7 @@ export const useTrafficStats = (
};
export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
const { result } = useTrafficSearch(
const { result } = useConnectionsConsumption(
chartDataSelection.grouping,
toDateRange(chartDataSelection, currentDate),
);
@ -80,10 +81,6 @@ export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
if (result.state !== 'success') {
return {
chartData: { datasets: [], labels: [] },
usageTotal: 0,
overageCost: 0,
estimatedMonthlyCost: 0,
requestSummaryUsage: 0,
};
}
const traffic = result.data;

View File

@ -0,0 +1,46 @@
import useSWR from 'swr';
import { useMemo } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import type { MeteredConnectionsSchema } from 'openapi';
export type MeteredConnectionsResponse = {
refetch: () => void;
result:
| { state: 'success'; data: MeteredConnectionsSchema }
| { state: 'error'; error: Error }
| { state: 'loading' };
};
export const useConnectionsConsumption = (
grouping: 'monthly' | 'daily',
{
from,
to,
}: {
from: string;
to: string;
},
): MeteredConnectionsResponse => {
const apiPath = `api/admin/metrics/connection?grouping=${grouping}&from=${from}&to=${to}`;
const { data, error, mutate } = useSWR(formatApiPath(apiPath), fetcher);
return useMemo(() => {
const result = data
? { state: 'success' as const, data: data }
: error
? { state: 'error' as const, error }
: { state: 'loading' as const };
return {
refetch: () => mutate(),
result,
};
}, [data, error, mutate]);
};
const fetcher = (path: string) => {
return fetch(path)
.then(handleErrorResponses('Metered Connections Metrics'))
.then((res) => res.json());
};