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:
parent
22f51df76c
commit
1b7f91cd4b
@ -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');
|
||||
|
||||
|
@ -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) =>
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
};
|
Loading…
Reference in New Issue
Block a user