mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +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 annotationPlugin from 'chartjs-plugin-annotation';
|
||||||
import { useChartDataSelection } from './hooks/useChartDataSelection';
|
import { useChartDataSelection } from './hooks/useChartDataSelection';
|
||||||
|
|
||||||
|
// TODO: consider renaming to Connection Consumption
|
||||||
export const BackendConnections: FC = () => {
|
export const BackendConnections: FC = () => {
|
||||||
usePageTitle('Network - Backend Connections');
|
usePageTitle('Network - Backend Connections');
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
|
import type {
|
||||||
|
MeteredConnectionsSchema,
|
||||||
|
TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
} from 'openapi';
|
||||||
import {
|
import {
|
||||||
toConnectionChartData,
|
toConnectionChartData,
|
||||||
toTrafficUsageChartData,
|
toTrafficUsageChartData,
|
||||||
@ -152,9 +155,9 @@ describe('toTrafficUsageChartData', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('toConnectionChartData', () => {
|
describe('toConnectionChartData', () => {
|
||||||
const dataPoint = (period: string, count: number) => ({
|
const dataPoint = (period: string, connections: number) => ({
|
||||||
period,
|
period,
|
||||||
trafficTypes: [{ count, group: 'successful-requests' }],
|
connections,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fromEndpointInfo = (endpoint: keyof typeof endpointsInfo) => {
|
const fromEndpointInfo = (endpoint: keyof typeof endpointsInfo) => {
|
||||||
@ -167,7 +170,7 @@ describe('toConnectionChartData', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('monthly data conversion', () => {
|
test('monthly data conversion', () => {
|
||||||
const input: TrafficUsageDataSegmentedCombinedSchema = {
|
const input: MeteredConnectionsSchema = {
|
||||||
grouping: 'monthly',
|
grouping: 'monthly',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
from: '2025-01-01',
|
from: '2025-01-01',
|
||||||
@ -175,16 +178,12 @@ describe('toConnectionChartData', () => {
|
|||||||
},
|
},
|
||||||
apiData: [
|
apiData: [
|
||||||
{
|
{
|
||||||
apiPath: '/api/admin', // filter out
|
meteredGroup: 'default',
|
||||||
dataPoints: [dataPoint('2025-06', 5)],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apiPath: '/api/client',
|
|
||||||
dataPoints: [
|
dataPoints: [
|
||||||
dataPoint('2025-06', 10 * 5 * 60 * 24 * 30),
|
dataPoint('2025-06', 10),
|
||||||
dataPoint('2025-01', 7 * 5 * 60 * 24 * 31),
|
dataPoint('2025-01', 7),
|
||||||
dataPoint('2025-03', 11 * 5 * 60 * 24 * 31),
|
dataPoint('2025-03', 11),
|
||||||
dataPoint('2025-04', 13 * 5 * 60 * 24 * 30),
|
dataPoint('2025-04', 13),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -194,7 +193,9 @@ describe('toConnectionChartData', () => {
|
|||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
data: [7, 0, 11, 13, 0, 10],
|
data: [7, 0, 11, 13, 0, 10],
|
||||||
...fromEndpointInfo('/api/client'),
|
hoverBackgroundColor: '#6D66D9',
|
||||||
|
label: 'Connections',
|
||||||
|
backgroundColor: '#6D66D9',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
labels: [
|
labels: [
|
||||||
@ -211,7 +212,7 @@ describe('toConnectionChartData', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('daily data conversion', () => {
|
test('daily data conversion', () => {
|
||||||
const input: TrafficUsageDataSegmentedCombinedSchema = {
|
const input: MeteredConnectionsSchema = {
|
||||||
grouping: 'daily',
|
grouping: 'daily',
|
||||||
dateRange: {
|
dateRange: {
|
||||||
from: '2025-01-01',
|
from: '2025-01-01',
|
||||||
@ -219,16 +220,12 @@ describe('toConnectionChartData', () => {
|
|||||||
},
|
},
|
||||||
apiData: [
|
apiData: [
|
||||||
{
|
{
|
||||||
apiPath: '/api/admin', // filter out
|
meteredGroup: 'default',
|
||||||
dataPoints: [dataPoint('2025-01-01', 5)],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apiPath: '/api/client',
|
|
||||||
dataPoints: [
|
dataPoints: [
|
||||||
dataPoint('2025-01-02', 2 * 5 * 60 * 24),
|
dataPoint('2025-01-02', 2),
|
||||||
dataPoint('2025-01-17', 6 * 5 * 60 * 24),
|
dataPoint('2025-01-17', 6),
|
||||||
dataPoint('2025-01-19', 4 * 5 * 60 * 24),
|
dataPoint('2025-01-19', 4),
|
||||||
dataPoint('2025-01-06', 8 * 5 * 60 * 24),
|
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, 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,
|
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) =>
|
labels: Array.from({ length: 31 }).map((_, index) =>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import type { ChartDataset } from 'chart.js';
|
import type { ChartDataset } from 'chart.js';
|
||||||
import type { TrafficUsageDataSegmentedCombinedSchema } from 'openapi';
|
import type {
|
||||||
|
MeteredConnectionsSchema,
|
||||||
|
TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
} from 'openapi';
|
||||||
import { endpointsInfo } from './endpoint-info';
|
import { endpointsInfo } from './endpoint-info';
|
||||||
import {
|
import {
|
||||||
addDays,
|
addDays,
|
||||||
addMonths,
|
addMonths,
|
||||||
differenceInCalendarDays,
|
differenceInCalendarDays,
|
||||||
differenceInCalendarMonths,
|
differenceInCalendarMonths,
|
||||||
getDaysInMonth,
|
|
||||||
parseISO,
|
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { formatDay, formatMonth } from './dates';
|
import { formatDay, formatMonth } from './dates';
|
||||||
import type { ChartDataSelection } from './chart-data-selection';
|
import type { ChartDataSelection } from './chart-data-selection';
|
||||||
@ -45,37 +46,21 @@ export const toTrafficUsageChartData = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const toConnectionChartData = (
|
export const toConnectionChartData = (
|
||||||
traffic: TrafficUsageDataSegmentedCombinedSchema,
|
traffic: MeteredConnectionsSchema,
|
||||||
): { datasets: ChartDatasetType[]; labels: string[] } => {
|
): { datasets: ChartDatasetType[]; labels: string[] } => {
|
||||||
const { newRecord, labels } = getLabelsAndRecords(traffic);
|
const { newRecord, labels } = getLabelsAndRecords(traffic);
|
||||||
const datasets = traffic.apiData
|
const datasets = traffic.apiData.map((item) => {
|
||||||
.filter((apiData) => apiData.apiPath === '/api/client')
|
|
||||||
.sort(
|
|
||||||
(item1, item2) =>
|
|
||||||
endpointsInfo[item1.apiPath].order -
|
|
||||||
endpointsInfo[item2.apiPath].order,
|
|
||||||
)
|
|
||||||
.map((item) => {
|
|
||||||
const record = newRecord();
|
const record = newRecord();
|
||||||
for (const dataPoint of Object.values(item.dataPoints)) {
|
for (const dataPoint of Object.values(item.dataPoints)) {
|
||||||
const date = parseISO(dataPoint.period);
|
const requestCount = dataPoint.connections;
|
||||||
const requestCount = dataPoint.trafficTypes[0].count;
|
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 = endpointsInfo[item.apiPath];
|
const epInfo = {
|
||||||
|
label: 'Connections',
|
||||||
|
color: '#6D66D9',
|
||||||
|
order: 1,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: epInfo.label,
|
label: epInfo.label,
|
||||||
@ -89,7 +74,10 @@ export const toConnectionChartData = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getLabelsAndRecords = (
|
const getLabelsAndRecords = (
|
||||||
traffic: TrafficUsageDataSegmentedCombinedSchema,
|
traffic: Pick<
|
||||||
|
TrafficUsageDataSegmentedCombinedSchema,
|
||||||
|
'dateRange' | 'grouping'
|
||||||
|
>,
|
||||||
) => {
|
) => {
|
||||||
if (traffic.grouping === 'monthly') {
|
if (traffic.grouping === 'monthly') {
|
||||||
const from = new Date(traffic.dateRange.from);
|
const from = new Date(traffic.dateRange.from);
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
} from 'utils/traffic-calculations';
|
} from 'utils/traffic-calculations';
|
||||||
import { BILLING_TRAFFIC_BUNDLE_PRICE } from '../../../billing/BillingDashboard/BillingPlan/BillingPlan';
|
import { BILLING_TRAFFIC_BUNDLE_PRICE } from '../../../billing/BillingDashboard/BillingPlan/BillingPlan';
|
||||||
import { averageTrafficPreviousMonths } from '../average-traffic-previous-months';
|
import { averageTrafficPreviousMonths } from '../average-traffic-previous-months';
|
||||||
|
import { useConnectionsConsumption } from 'hooks/api/getters/useConnectionsConsumption/useConnectionsConsumption';
|
||||||
|
|
||||||
export const useTrafficStats = (
|
export const useTrafficStats = (
|
||||||
includedTraffic: number,
|
includedTraffic: number,
|
||||||
@ -72,7 +73,7 @@ export const useTrafficStats = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
|
export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
|
||||||
const { result } = useTrafficSearch(
|
const { result } = useConnectionsConsumption(
|
||||||
chartDataSelection.grouping,
|
chartDataSelection.grouping,
|
||||||
toDateRange(chartDataSelection, currentDate),
|
toDateRange(chartDataSelection, currentDate),
|
||||||
);
|
);
|
||||||
@ -80,10 +81,6 @@ export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
|
|||||||
if (result.state !== 'success') {
|
if (result.state !== 'success') {
|
||||||
return {
|
return {
|
||||||
chartData: { datasets: [], labels: [] },
|
chartData: { datasets: [], labels: [] },
|
||||||
usageTotal: 0,
|
|
||||||
overageCost: 0,
|
|
||||||
estimatedMonthlyCost: 0,
|
|
||||||
requestSummaryUsage: 0,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const traffic = result.data;
|
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