Skip to content

Commit

Permalink
chore: add traffic overage banner
Browse files Browse the repository at this point in the history
  • Loading branch information
nunogois committed Nov 21, 2024
1 parent 6d75ad7 commit d609315
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 19 deletions.
2 changes: 2 additions & 0 deletions frontend/src/component/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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': {
Expand Down Expand Up @@ -68,6 +69,7 @@ export const App = () => {
/>
<LicenseBanner />
<SecurityBanner />
<TrafficOverageBanner />
<ExternalBanners />
<InternalBanners />
<StyledContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
76 changes: 76 additions & 0 deletions frontend/src/component/banners/TrafficOverageBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 84 additions & 0 deletions frontend/src/component/banners/TrafficOverageBanner.tsx
Original file line number Diff line number Diff line change
@@ -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',
}}
/>
);
};
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand Down

0 comments on commit d609315

Please sign in to comment.