Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/api-v4/src/quotas/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import type { ObjectStorageEndpointTypes } from 'src/object-storage';
import type { Region } from 'src/regions';

export enum QuotaResourceMetrics {
BUCKET = 'bucket',
BYTE = 'byte',
BYTE_PER_SECOND = 'byte_per_second',
CLUSTER = 'cluster',
CPU = 'CPU',
GPU = 'GPU',
OBJECT = 'object',
REQUEST = 'request',
VPU = 'VPU',
}

/**
* A Quota is a service used limit that is rated based on service metrics such
* as vCPUs used, instances or storage size.
Expand Down Expand Up @@ -54,7 +67,7 @@ export interface Quota {
/**
* The unit of measurement for this service limit.
*/
resource_metric: string;
resource_metric: QuotaResourceMetrics;

/**
* The S3 endpoint URL to which this limit applies.
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-13447-added-1773052533338.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Quota resource metrics enum ([#13447](https://github.com/linode/manager/pull/13447))
13 changes: 7 additions & 6 deletions packages/manager/cypress/e2e/core/account/quotas-storage.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import { regionFactory } from '@linode/utilities';
import { profileFactory } from '@linode/utilities';
import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';
Expand Down Expand Up @@ -74,7 +75,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 10,
quota_name: randomLabel(15),
resource_metric: 'byte',
resource_metric: QuotaResourceMetrics.BYTE,
s3_endpoint: selectedDomain,
}),
quotaFactory.build({
Expand All @@ -84,7 +85,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 78,
quota_name: randomLabel(15),
resource_metric: 'bucket',
resource_metric: QuotaResourceMetrics.BUCKET,
s3_endpoint: selectedDomain,
}),
quotaFactory.build({
Expand All @@ -94,7 +95,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 400,
quota_name: randomLabel(15),
resource_metric: 'object',
resource_metric: QuotaResourceMetrics.OBJECT,
s3_endpoint: selectedDomain,
}),
];
Expand Down Expand Up @@ -229,7 +230,7 @@ describe('Quota workflow tests', () => {
endpoint_type: updatedEndpoint.endpoint_type,
quota_limit: 20,
quota_name: randomLabel(15),
resource_metric: 'byte',
resource_metric: QuotaResourceMetrics.BYTE,
s3_endpoint: updatedDomain,
}),
quotaFactory.build({
Expand All @@ -239,7 +240,7 @@ describe('Quota workflow tests', () => {
endpoint_type: updatedEndpoint.endpoint_type,
quota_limit: 122,
quota_name: randomLabel(15),
resource_metric: 'bucket',
resource_metric: QuotaResourceMetrics.BUCKET,
s3_endpoint: updatedDomain,
}),
quotaFactory.build({
Expand All @@ -249,7 +250,7 @@ describe('Quota workflow tests', () => {
endpoint_type: updatedEndpoint.endpoint_type,
quota_limit: 450,
quota_name: randomLabel(15),
resource_metric: 'object',
resource_metric: QuotaResourceMetrics.OBJECT,
s3_endpoint: updatedDomain,
}),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import { regionFactory } from '@linode/utilities';
import { authenticate } from 'support/api/authentication';
import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';
Expand Down Expand Up @@ -58,7 +59,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 10,
quota_name: 'Total Capacity',
resource_metric: 'byte',
resource_metric: QuotaResourceMetrics.BYTE,
s3_endpoint: selectedDomain,
}),
quotaFactory.build({
Expand All @@ -68,7 +69,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 78,
quota_name: 'Number of Objects',
resource_metric: 'bucket',
resource_metric: QuotaResourceMetrics.BUCKET,
s3_endpoint: selectedDomain,
}),
quotaFactory.build({
Expand All @@ -78,7 +79,7 @@ const mockQuotas = [
endpoint_type: mockSelectedEndpoint.endpoint_type,
quota_limit: 400,
quota_name: 'Number of Buckets',
resource_metric: 'object',
resource_metric: QuotaResourceMetrics.OBJECT,
s3_endpoint: selectedDomain,
}),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';
Expand Down Expand Up @@ -43,7 +44,11 @@ describe('QuotaUsageBanner', () => {
'should display correct byte quota usage text for $usage bytes used out of $limit bytes',
({ usage, limit, expectedText }) => {
const { getByText } = renderWithTheme(
<QuotaUsageBar limit={limit} resourceMetric="byte" usage={usage} />
<QuotaUsageBar
limit={limit}
resourceMetric={QuotaResourceMetrics.BYTE}
usage={usage}
/>
);
const quotaUsageText = getByText(expectedText);
expect(quotaUsageText).toBeVisible();
Expand Down
11 changes: 5 additions & 6 deletions packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { Typography, useTheme } from '@linode/ui';
import * as React from 'react';

import { BarPercent } from 'src/components/BarPercent';
import {
convertResourceMetric,
pluralizeMetric,
} from 'src/features/Account/Quotas/utils';
import { convertResourceMetric } from 'src/features/Account/Quotas/utils';

import type { QuotaResourceMetrics } from '@linode/api-v4';

interface Props {
limit: number;
resourceMetric: string;
resourceMetric: QuotaResourceMetrics;
usage: number;
}

Expand All @@ -18,7 +17,7 @@ export const QuotaUsageBar = ({ limit, usage, resourceMetric }: Props) => {

const { convertedUsage, convertedLimit, convertedResourceMetric } =
convertResourceMetric({
initialResourceMetric: pluralizeMetric(limit, resourceMetric),
initialResourceMetric: resourceMetric,
initialUsage: usage,
initialLimit: limit,
});
Expand Down
3 changes: 2 additions & 1 deletion packages/manager/src/factories/quotas.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4/lib/quotas/types';
import { Factory } from '@linode/utilities';

import type { Quota, QuotaUsage } from '@linode/api-v4/lib/quotas/types';
Expand All @@ -9,7 +10,7 @@ export const quotaFactory = Factory.Sync.makeFactory<Quota>({
quota_name: 'Linode Dedicated vCPUs',
quota_type: 'linode-dedicated-cpus',
region_applied: 'us-east',
resource_metric: 'CPU',
resource_metric: QuotaResourceMetrics.CPU,
has_usage: true,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import { waitFor } from '@testing-library/react';
import * as React from 'react';

Expand Down Expand Up @@ -131,7 +132,7 @@ describe('QuotasTable', () => {
'Current total ingress bandwidth per account, per endpoint',
quota_limit: 1250000000,
quota_type: 'obj-total-ingress-throughput',
resource_metric: 'byte_per_second',
resource_metric: QuotaResourceMetrics.BYTE_PER_SECOND,
has_usage: false,
}),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ import { TableRow } from 'src/components/TableRow/TableRow';
import { useFlags } from 'src/hooks/useFlags';
import { useIsAkamaiAccount } from 'src/hooks/useIsAkamaiAccount';

import {
convertResourceMetric,
getQuotaError,
pluralizeMetric,
} from '../utils';
import { convertResourceMetric, getQuotaError } from '../utils';

import type { QuotaWithUsage } from '../utils';
import type { Quota, QuotaUsage } from '@linode/api-v4';
Expand Down Expand Up @@ -58,10 +54,7 @@ export const QuotasTableRow = (props: QuotasTableRowProps) => {
isAkamaiAccount);

const { convertedLimit, convertedResourceMetric } = convertResourceMetric({
initialResourceMetric: pluralizeMetric(
quota.quota_limit,
quota.resource_metric
),
initialResourceMetric: quota.resource_metric,
initialUsage: quota.usage?.usage ?? 0,
initialLimit: quota.quota_limit,
});
Expand Down
18 changes: 5 additions & 13 deletions packages/manager/src/features/Account/Quotas/utils.test.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import { profileFactory } from '@linode/utilities';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react';
Expand Down Expand Up @@ -133,7 +134,7 @@ describe('useGetLocationsForQuotaService', () => {

describe('convertResourceMetric', () => {
it('should convert the resource metric to a human readable format', () => {
const resourceMetric = 'byte';
const resourceMetric = QuotaResourceMetrics.BYTE;
const usage = 1e6;
const limit = 1e8;

Expand All @@ -154,25 +155,16 @@ describe('convertResourceMetric', () => {
describe('pluralizeMetric', () => {
it('should not pluralize if the value is 1', () => {
const value = 1;
const unit = 'CPU';
const unit = QuotaResourceMetrics.CPU;

const result = pluralizeMetric(value, unit);

expect(result).toEqual('CPU');
});

it('should not pluralize the resource metric if the unit is byte', () => {
it('should pluralize the resource metric if the value is greater than 1', () => {
const value = 100;
const unit = 'byte';

const result = pluralizeMetric(value, unit);

expect(result).toEqual('byte');
});

it('should pluralize the resource metric if the unit is not byte', () => {
const value = 100;
const unit = 'CPU';
const unit = QuotaResourceMetrics.CPU;

const result = pluralizeMetric(value, unit);

Expand Down
74 changes: 37 additions & 37 deletions packages/manager/src/features/Account/Quotas/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import { useRegionsQuery } from '@linode/queries';
import { capitalize, readableBytes } from '@linode/utilities';
import { object, string } from 'yup';
Expand Down Expand Up @@ -217,7 +218,7 @@ export const getQuotaIncreaseMessage = ({

interface ConvertResourceMetricProps {
initialLimit: number;
initialResourceMetric: string;
initialResourceMetric: QuotaResourceMetrics;
initialUsage: number;
}

Expand All @@ -233,34 +234,38 @@ export const convertResourceMetric = ({
convertedResourceMetric: string;
convertedUsage: number;
} => {
if (initialResourceMetric === 'byte') {
const limitReadable = readableBytes(initialLimit);
switch (initialResourceMetric) {
case QuotaResourceMetrics.BYTE: {
const limitReadable = readableBytes(initialLimit);

return {
convertedUsage: readableBytes(initialUsage, {
unit: limitReadable.unit,
}).value,
convertedResourceMetric: capitalize(limitReadable.unit),
convertedLimit: limitReadable.value,
};
}

if (initialResourceMetric === 'byte_per_second') {
return {
convertedUsage: 0,
convertedResourceMetric: 'Gbps',
convertedLimit: readableBytes(initialLimit * 8, {
unit: 'GB',
base10: true,
}).value,
};
return {
convertedUsage: readableBytes(initialUsage, {
unit: limitReadable.unit,
}).value,
convertedLimit: limitReadable.value,
convertedResourceMetric: capitalize(limitReadable.unit),
};
}
case QuotaResourceMetrics.BYTE_PER_SECOND: {
return {
convertedUsage: 0,
convertedResourceMetric: 'Gbps',
convertedLimit: readableBytes(initialLimit * 8, {
unit: 'GB',
base10: true,
}).value,
};
}
default: {
return {
convertedUsage: initialUsage,
convertedLimit: initialLimit,
convertedResourceMetric: capitalize(
pluralizeMetric(initialLimit, initialResourceMetric)
),
};
}
}

return {
convertedUsage: initialUsage,
convertedLimit: initialLimit,
convertedResourceMetric: capitalize(initialResourceMetric),
};
};

/**
Expand All @@ -270,16 +275,11 @@ export const convertResourceMetric = ({
*
* Note: the value should be the raw values in bytes, not an existing conversion
*/
export const pluralizeMetric = (value: number, unit: string) => {
if (unit === 'byte_per_second') {
return unit;
}

if (unit !== 'byte') {
return value > 1 ? `${unit}s` : unit;
}

return unit;
export const pluralizeMetric = (
value: number,
unit: QuotaResourceMetrics
): string => {
return value > 1 ? `${unit}s` : unit;
};

export const getQuotaIncreaseFormSchema = (currentLimit: number) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuotaResourceMetrics } from '@linode/api-v4';
import * as React from 'react';

import { quotaFactory, quotaUsageFactory } from 'src/factories/quotas';
Expand Down Expand Up @@ -45,7 +46,7 @@ const quotasMock = [
s3_endpoint: testEndpoint,
description: 'Current number of buckets per account, per endpoint',
quota_limit: 10,
resource_metric: 'bucket',
resource_metric: QuotaResourceMetrics.BUCKET,
}),
quotaFactory.build({
quota_id: `obj-bytes-${testEndpoint}`,
Expand All @@ -55,7 +56,7 @@ const quotasMock = [
s3_endpoint: testEndpoint,
description: 'Current total capacity per account, per endpoint',
quota_limit: 2048,
resource_metric: 'byte',
resource_metric: QuotaResourceMetrics.BYTE,
}),
quotaFactory.build({
quota_id: `obj-objects-${testEndpoint}`,
Expand All @@ -65,7 +66,7 @@ const quotasMock = [
s3_endpoint: testEndpoint,
description: 'Current number of objects per account, per endpoint',
quota_limit: 10,
resource_metric: 'object',
resource_metric: QuotaResourceMetrics.OBJECT,
}),
];

Expand Down
Loading
Loading