mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
chore: add traffic overage banner
This commit is contained in:
parent
6d75ad73d4
commit
d609315918
@ -23,6 +23,7 @@ import { LicenseBanner } from './banners/internalBanners/LicenseBanner';
|
||||
import { Demo } from './demo/Demo';
|
||||
import { LoginRedirect } from './common/LoginRedirect/LoginRedirect';
|
||||
import { SecurityBanner } from './banners/internalBanners/SecurityBanner';
|
||||
import { TrafficOverageBanner } from './banners/TrafficOverageBanner';
|
||||
|
||||
const StyledContainer = styled('div')(() => ({
|
||||
'& ul': {
|
||||
@ -68,6 +69,7 @@ export const App = () => {
|
||||
/>
|
||||
<LicenseBanner />
|
||||
<SecurityBanner />
|
||||
<TrafficOverageBanner />
|
||||
<ExternalBanners />
|
||||
<InternalBanners />
|
||||
<StyledContainer>
|
||||
|
@ -239,20 +239,11 @@ export const NetworkTrafficUsage: VFC = () => {
|
||||
elseShow={
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={includedTraffic > 0 && overageCost > 0}
|
||||
condition={includedTraffic > 0}
|
||||
show={
|
||||
<Alert severity='warning' sx={{ mb: 4 }}>
|
||||
<b>Heads up!</b> You are currently consuming
|
||||
more requests than your plan includes and will
|
||||
be billed according to our terms. Please see{' '}
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to='https://www.getunleash.io/pricing'
|
||||
>
|
||||
this page
|
||||
</Link>{' '}
|
||||
for more information. In order to reduce your
|
||||
traffic consumption, you may configure an{' '}
|
||||
<Alert severity='info' sx={{ mb: 4 }}>
|
||||
In order to reduce your traffic consumption,
|
||||
consider setting up an{' '}
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to='https://docs.getunleash.io/reference/unleash-edge'
|
||||
|
76
frontend/src/component/banners/TrafficOverageBanner.test.tsx
Normal file
76
frontend/src/component/banners/TrafficOverageBanner.test.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import { TrafficOverageBanner } from './TrafficOverageBanner';
|
||||
import { toSelectablePeriod } from 'hooks/useTrafficData';
|
||||
import { BILLING_INCLUDED_REQUESTS } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
const setupApi = (totalRequests = 0) => {
|
||||
const period = toSelectablePeriod(new Date()).key;
|
||||
|
||||
testServerRoute(server, '/api/admin/ui-config', {
|
||||
environment: 'Enterprise',
|
||||
versionInfo: {
|
||||
current: {
|
||||
enterprise: 'Enterprise',
|
||||
},
|
||||
},
|
||||
billing: 'pay-as-you-go',
|
||||
flags: {
|
||||
'enterprise-payg': true,
|
||||
estimateTrafficDataCost: true,
|
||||
},
|
||||
});
|
||||
|
||||
testServerRoute(server, `/api/admin/metrics/traffic/${period}`, {
|
||||
period,
|
||||
apiData: [
|
||||
{
|
||||
apiPath: '/api/client',
|
||||
days: [
|
||||
{
|
||||
day: `${period}-01T00:00:00.000Z`,
|
||||
trafficTypes: [
|
||||
{
|
||||
group: 'successful-requests',
|
||||
count: totalRequests,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
test('Displays overage banner when overage cost is calculated', async () => {
|
||||
setupApi(BILLING_INCLUDED_REQUESTS + 1_000_000);
|
||||
|
||||
render(<TrafficOverageBanner />);
|
||||
|
||||
const bannerMessage = await screen.findByText(
|
||||
/You're using more requests than your plan/,
|
||||
);
|
||||
expect(bannerMessage).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Displays estimated monthly cost banner when usage is projected to exceed', async () => {
|
||||
setupApi(BILLING_INCLUDED_REQUESTS - 1_000_000);
|
||||
|
||||
render(<TrafficOverageBanner />);
|
||||
|
||||
const bannerMessage = await screen.findByText(
|
||||
/Based on your current usage, you're projected to exceed your plan/,
|
||||
);
|
||||
expect(bannerMessage).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Does not display banner when no overage or estimated cost', async () => {
|
||||
setupApi();
|
||||
|
||||
render(<TrafficOverageBanner />);
|
||||
|
||||
expect(screen.queryByText('Heads up!')).not.toBeInTheDocument();
|
||||
});
|
84
frontend/src/component/banners/TrafficOverageBanner.tsx
Normal file
84
frontend/src/component/banners/TrafficOverageBanner.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { Banner } from './Banner/Banner';
|
||||
import { useTrafficDataEstimation } from 'hooks/useTrafficData';
|
||||
import { useTrafficLimit } from 'component/admin/network/NetworkTrafficUsage/hooks/useTrafficLimit';
|
||||
import { useInstanceTrafficMetrics } from 'hooks/api/getters/useInstanceTrafficMetrics/useInstanceTrafficMetrics';
|
||||
import { BILLING_TRAFFIC_BUNDLE_PRICE } from 'component/admin/billing/BillingDashboard/BillingPlan/BillingPlan';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const TrafficOverageBanner = () => {
|
||||
const estimateTrafficDataCostEnabled = useUiFlag('estimateTrafficDataCost');
|
||||
const includedTraffic = useTrafficLimit();
|
||||
const {
|
||||
currentPeriod,
|
||||
toChartData,
|
||||
toTrafficUsageSum,
|
||||
endpointsInfo,
|
||||
getDayLabels,
|
||||
calculateOverageCost,
|
||||
calculateEstimatedMonthlyCost,
|
||||
} = useTrafficDataEstimation();
|
||||
const traffic = useInstanceTrafficMetrics(currentPeriod.key);
|
||||
|
||||
const trafficData = useMemo(
|
||||
() =>
|
||||
toChartData(
|
||||
getDayLabels(currentPeriod.dayCount),
|
||||
traffic,
|
||||
endpointsInfo,
|
||||
),
|
||||
[traffic, currentPeriod, endpointsInfo],
|
||||
);
|
||||
|
||||
const calculatedOverageCost = useMemo(
|
||||
() =>
|
||||
includedTraffic
|
||||
? calculateOverageCost(
|
||||
toTrafficUsageSum(trafficData),
|
||||
includedTraffic,
|
||||
BILLING_TRAFFIC_BUNDLE_PRICE,
|
||||
)
|
||||
: 0,
|
||||
[includedTraffic, trafficData],
|
||||
);
|
||||
|
||||
const estimatedMonthlyCost = useMemo(
|
||||
() =>
|
||||
includedTraffic && estimateTrafficDataCostEnabled
|
||||
? calculateEstimatedMonthlyCost(
|
||||
currentPeriod.key,
|
||||
trafficData,
|
||||
includedTraffic,
|
||||
new Date(),
|
||||
BILLING_TRAFFIC_BUNDLE_PRICE,
|
||||
)
|
||||
: 0,
|
||||
[
|
||||
includedTraffic,
|
||||
estimateTrafficDataCostEnabled,
|
||||
trafficData,
|
||||
currentPeriod,
|
||||
],
|
||||
);
|
||||
|
||||
const overageMessage =
|
||||
calculatedOverageCost > 0
|
||||
? `**Heads up!** You're using more requests than your plan [includes](https://www.getunleash.io/pricing), and additional charges will apply per our [terms](https://www.getunleash.io/fair-use-policy).`
|
||||
: estimatedMonthlyCost > 0
|
||||
? `**Heads up!** Based on your current usage, you're projected to exceed your plan's [limit](https://www.getunleash.io/pricing), and additional charges may apply per our [terms](https://www.getunleash.io/fair-use-policy).`
|
||||
: undefined;
|
||||
|
||||
if (!overageMessage) return null;
|
||||
|
||||
return (
|
||||
<Banner
|
||||
banner={{
|
||||
message: overageMessage,
|
||||
variant: 'warning',
|
||||
sticky: true,
|
||||
link: '/admin/network/data-usage',
|
||||
linkText: 'See data usage',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,8 +1,14 @@
|
||||
import useSWR from 'swr';
|
||||
import { useMemo } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import type { TrafficUsageDataSegmentedSchema } from 'openapi';
|
||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||
|
||||
const DEFAULT_DATA: TrafficUsageDataSegmentedSchema = {
|
||||
apiData: [],
|
||||
period: '',
|
||||
};
|
||||
|
||||
export interface IInstanceTrafficMetricsResponse {
|
||||
usage: TrafficUsageDataSegmentedSchema;
|
||||
@ -17,14 +23,21 @@ export interface IInstanceTrafficMetricsResponse {
|
||||
export const useInstanceTrafficMetrics = (
|
||||
period: string,
|
||||
): IInstanceTrafficMetricsResponse => {
|
||||
const { data, error, mutate } = useSWR(
|
||||
formatApiPath(`api/admin/metrics/traffic/${period}`),
|
||||
fetcher,
|
||||
);
|
||||
const {
|
||||
isPro,
|
||||
uiConfig: { billing },
|
||||
} = useUiConfig();
|
||||
const { data, error, mutate } =
|
||||
useConditionalSWR<TrafficUsageDataSegmentedSchema>(
|
||||
isPro() || billing === 'pay-as-you-go',
|
||||
DEFAULT_DATA,
|
||||
formatApiPath(`api/admin/metrics/traffic/${period}`),
|
||||
fetcher,
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
usage: data,
|
||||
usage: data ?? DEFAULT_DATA,
|
||||
loading: !error && !data,
|
||||
refetch: () => mutate(),
|
||||
error,
|
||||
|
Loading…
Reference in New Issue
Block a user