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 { Demo } from './demo/Demo';
|
||||||
import { LoginRedirect } from './common/LoginRedirect/LoginRedirect';
|
import { LoginRedirect } from './common/LoginRedirect/LoginRedirect';
|
||||||
import { SecurityBanner } from './banners/internalBanners/SecurityBanner';
|
import { SecurityBanner } from './banners/internalBanners/SecurityBanner';
|
||||||
|
import { TrafficOverageBanner } from './banners/TrafficOverageBanner';
|
||||||
|
|
||||||
const StyledContainer = styled('div')(() => ({
|
const StyledContainer = styled('div')(() => ({
|
||||||
'& ul': {
|
'& ul': {
|
||||||
@ -68,6 +69,7 @@ export const App = () => {
|
|||||||
/>
|
/>
|
||||||
<LicenseBanner />
|
<LicenseBanner />
|
||||||
<SecurityBanner />
|
<SecurityBanner />
|
||||||
|
<TrafficOverageBanner />
|
||||||
<ExternalBanners />
|
<ExternalBanners />
|
||||||
<InternalBanners />
|
<InternalBanners />
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
@ -239,20 +239,11 @@ export const NetworkTrafficUsage: VFC = () => {
|
|||||||
elseShow={
|
elseShow={
|
||||||
<>
|
<>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={includedTraffic > 0 && overageCost > 0}
|
condition={includedTraffic > 0}
|
||||||
show={
|
show={
|
||||||
<Alert severity='warning' sx={{ mb: 4 }}>
|
<Alert severity='info' sx={{ mb: 4 }}>
|
||||||
<b>Heads up!</b> You are currently consuming
|
In order to reduce your traffic consumption,
|
||||||
more requests than your plan includes and will
|
consider setting up an{' '}
|
||||||
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{' '}
|
|
||||||
<Link
|
<Link
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to='https://docs.getunleash.io/reference/unleash-edge'
|
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 { useMemo } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import type { TrafficUsageDataSegmentedSchema } from 'openapi';
|
import type { TrafficUsageDataSegmentedSchema } from 'openapi';
|
||||||
|
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||||
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
|
|
||||||
|
const DEFAULT_DATA: TrafficUsageDataSegmentedSchema = {
|
||||||
|
apiData: [],
|
||||||
|
period: '',
|
||||||
|
};
|
||||||
|
|
||||||
export interface IInstanceTrafficMetricsResponse {
|
export interface IInstanceTrafficMetricsResponse {
|
||||||
usage: TrafficUsageDataSegmentedSchema;
|
usage: TrafficUsageDataSegmentedSchema;
|
||||||
@ -17,14 +23,21 @@ export interface IInstanceTrafficMetricsResponse {
|
|||||||
export const useInstanceTrafficMetrics = (
|
export const useInstanceTrafficMetrics = (
|
||||||
period: string,
|
period: string,
|
||||||
): IInstanceTrafficMetricsResponse => {
|
): IInstanceTrafficMetricsResponse => {
|
||||||
const { data, error, mutate } = useSWR(
|
const {
|
||||||
formatApiPath(`api/admin/metrics/traffic/${period}`),
|
isPro,
|
||||||
fetcher,
|
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(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
usage: data,
|
usage: data ?? DEFAULT_DATA,
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
|
Loading…
Reference in New Issue
Block a user