mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: requests consumption UI for frontend (#9550)
Switching frontend traffic tab to use the requests consumption API:
This commit is contained in:
parent
f093a3f4b3
commit
6d6a4290fe
@ -18,7 +18,7 @@ import annotationPlugin from 'chartjs-plugin-annotation';
|
||||
import { customHighlightPlugin } from 'component/common/Chart/customHighlightPlugin';
|
||||
import { PeriodSelector } from './PeriodSelector';
|
||||
import { getChartLabel } from './chart-functions';
|
||||
import { useTrafficStats } from './hooks/useStats';
|
||||
import { useRequestsStats } from './hooks/useStats';
|
||||
import { StyledBox, TopRow } from './SharedComponents';
|
||||
import { useChartDataSelection } from './hooks/useChartDataSelection';
|
||||
|
||||
@ -30,12 +30,7 @@ const FrontendNetworkTrafficUsage: FC = () => {
|
||||
const { chartDataSelection, setChartDataSelection, options } =
|
||||
useChartDataSelection();
|
||||
|
||||
const includedTraffic = 0;
|
||||
const { chartData } = useTrafficStats(
|
||||
includedTraffic,
|
||||
chartDataSelection,
|
||||
'/api/frontend',
|
||||
);
|
||||
const { chartData } = useRequestsStats(chartDataSelection);
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type {
|
||||
MeteredConnectionsSchema,
|
||||
MeteredRequestsSchema,
|
||||
TrafficUsageDataSegmentedCombinedSchema,
|
||||
} from 'openapi';
|
||||
import {
|
||||
toConnectionChartData,
|
||||
toRequestChartData,
|
||||
toTrafficUsageChartData,
|
||||
} from './chart-functions';
|
||||
import { endpointsInfo } from './endpoint-info';
|
||||
@ -251,3 +253,92 @@ describe('toConnectionChartData', () => {
|
||||
expect(toConnectionChartData(input)).toMatchObject(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toRequestChartData', () => {
|
||||
const dataPoint = (period: string, requests: number) => ({
|
||||
period,
|
||||
requests,
|
||||
});
|
||||
|
||||
test('monthly data conversion', () => {
|
||||
const input: MeteredRequestsSchema = {
|
||||
grouping: 'monthly',
|
||||
dateRange: {
|
||||
from: '2025-01-01',
|
||||
to: '2025-06-30',
|
||||
},
|
||||
apiData: [
|
||||
{
|
||||
meteredGroup: 'default',
|
||||
dataPoints: [
|
||||
dataPoint('2025-06', 15),
|
||||
dataPoint('2025-01', 9),
|
||||
dataPoint('2025-03', 14),
|
||||
dataPoint('2025-04', 18),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expectedOutput = {
|
||||
datasets: [
|
||||
{
|
||||
data: [9, 0, 14, 18, 0, 15],
|
||||
hoverBackgroundColor: '#A39EFF',
|
||||
label: 'Frontend requests',
|
||||
backgroundColor: '#A39EFF',
|
||||
},
|
||||
],
|
||||
labels: [
|
||||
'2025-01',
|
||||
'2025-02',
|
||||
'2025-03',
|
||||
'2025-04',
|
||||
'2025-05',
|
||||
'Current month',
|
||||
],
|
||||
};
|
||||
|
||||
expect(toRequestChartData(input)).toMatchObject(expectedOutput);
|
||||
});
|
||||
|
||||
test('daily data conversion', () => {
|
||||
const input: MeteredRequestsSchema = {
|
||||
grouping: 'daily',
|
||||
dateRange: {
|
||||
from: '2025-01-01',
|
||||
to: '2025-01-31',
|
||||
},
|
||||
apiData: [
|
||||
{
|
||||
meteredGroup: 'default',
|
||||
dataPoints: [
|
||||
dataPoint('2025-01-02', 3),
|
||||
dataPoint('2025-01-17', 7),
|
||||
dataPoint('2025-01-19', 5),
|
||||
dataPoint('2025-01-06', 10),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expectedOutput = {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
0, 3, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,
|
||||
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
hoverBackgroundColor: '#A39EFF',
|
||||
label: 'Frontend requests',
|
||||
backgroundColor: '#A39EFF',
|
||||
},
|
||||
],
|
||||
labels: Array.from({ length: 31 }).map((_, index) =>
|
||||
(index + 1).toString(),
|
||||
),
|
||||
};
|
||||
|
||||
expect(toRequestChartData(input)).toMatchObject(expectedOutput);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { ChartDataset } from 'chart.js';
|
||||
import type {
|
||||
MeteredConnectionsSchema,
|
||||
MeteredRequestsSchema,
|
||||
TrafficUsageDataSegmentedCombinedSchema,
|
||||
} from 'openapi';
|
||||
import { endpointsInfo } from './endpoint-info';
|
||||
@ -73,6 +74,34 @@ export const toConnectionChartData = (
|
||||
return { datasets, labels };
|
||||
};
|
||||
|
||||
export const toRequestChartData = (
|
||||
traffic: MeteredRequestsSchema,
|
||||
): { datasets: ChartDatasetType[]; labels: string[] } => {
|
||||
const { newRecord, labels } = getLabelsAndRecords(traffic);
|
||||
const datasets = traffic.apiData.map((item) => {
|
||||
const record = newRecord();
|
||||
for (const dataPoint of Object.values(item.dataPoints)) {
|
||||
const requestCount = dataPoint.requests;
|
||||
record[dataPoint.period] = requestCount;
|
||||
}
|
||||
|
||||
const epInfo = {
|
||||
label: 'Frontend requests',
|
||||
color: '#A39EFF',
|
||||
order: 1,
|
||||
};
|
||||
|
||||
return {
|
||||
label: epInfo.label,
|
||||
data: Object.values(record),
|
||||
backgroundColor: epInfo.color,
|
||||
hoverBackgroundColor: epInfo.color,
|
||||
};
|
||||
});
|
||||
|
||||
return { datasets, labels };
|
||||
};
|
||||
|
||||
const getLabelsAndRecords = (
|
||||
traffic: Pick<
|
||||
TrafficUsageDataSegmentedCombinedSchema,
|
||||
|
@ -4,6 +4,7 @@ import { currentDate } from '../dates';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
toConnectionChartData,
|
||||
toRequestChartData,
|
||||
toTrafficUsageChartData,
|
||||
} from '../chart-functions';
|
||||
import {
|
||||
@ -14,6 +15,7 @@ import {
|
||||
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';
|
||||
import { useRequestsConsumption } from 'hooks/api/getters/useRequestsConsumption/useRequestsConsumption';
|
||||
|
||||
export const useTrafficStats = (
|
||||
includedTraffic: number,
|
||||
@ -94,3 +96,26 @@ export const useConsumptionStats = (chartDataSelection: ChartDataSelection) => {
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export const useRequestsStats = (chartDataSelection: ChartDataSelection) => {
|
||||
const { result } = useRequestsConsumption(
|
||||
chartDataSelection.grouping,
|
||||
toDateRange(chartDataSelection, currentDate),
|
||||
);
|
||||
const results = useMemo(() => {
|
||||
if (result.state !== 'success') {
|
||||
return {
|
||||
chartData: { datasets: [], labels: [] },
|
||||
};
|
||||
}
|
||||
const traffic = result.data;
|
||||
|
||||
const chartData = toRequestChartData(traffic);
|
||||
|
||||
return {
|
||||
chartData,
|
||||
};
|
||||
}, [JSON.stringify(result), JSON.stringify(chartDataSelection)]);
|
||||
|
||||
return results;
|
||||
};
|
||||
|
@ -0,0 +1,46 @@
|
||||
import useSWR from 'swr';
|
||||
import { useMemo } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import type { MeteredRequestsSchema } from 'openapi';
|
||||
|
||||
export type MeteredRequestsResponse = {
|
||||
refetch: () => void;
|
||||
result:
|
||||
| { state: 'success'; data: MeteredRequestsSchema }
|
||||
| { state: 'error'; error: Error }
|
||||
| { state: 'loading' };
|
||||
};
|
||||
|
||||
export const useRequestsConsumption = (
|
||||
grouping: 'monthly' | 'daily',
|
||||
{
|
||||
from,
|
||||
to,
|
||||
}: {
|
||||
from: string;
|
||||
to: string;
|
||||
},
|
||||
): MeteredRequestsResponse => {
|
||||
const apiPath = `api/admin/metrics/request?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('Consumption Requests Metrics'))
|
||||
.then((res) => res.json());
|
||||
};
|
@ -135,7 +135,7 @@ const sidebars: SidebarsConfig = {
|
||||
type: 'doc',
|
||||
label: 'Security and Compliance',
|
||||
id: 'feature-flag-tutorials/use-cases/security-compliance',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user