diff --git a/packages/api-v4/src/quotas/types.ts b/packages/api-v4/src/quotas/types.ts
index fe094430757..c21a9beff78 100644
--- a/packages/api-v4/src/quotas/types.ts
+++ b/packages/api-v4/src/quotas/types.ts
@@ -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.
@@ -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.
diff --git a/packages/manager/.changeset/pr-13447-added-1773052533338.md b/packages/manager/.changeset/pr-13447-added-1773052533338.md
new file mode 100644
index 00000000000..f2e06b74b95
--- /dev/null
+++ b/packages/manager/.changeset/pr-13447-added-1773052533338.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Added
+---
+
+Quota resource metrics enum ([#13447](https://github.com/linode/manager/pull/13447))
diff --git a/packages/manager/cypress/e2e/core/account/quotas-storage.spec.ts b/packages/manager/cypress/e2e/core/account/quotas-storage.spec.ts
index 411e0e157e2..4962af2027d 100644
--- a/packages/manager/cypress/e2e/core/account/quotas-storage.spec.ts
+++ b/packages/manager/cypress/e2e/core/account/quotas-storage.spec.ts
@@ -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';
@@ -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({
@@ -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({
@@ -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,
}),
];
@@ -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({
@@ -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({
@@ -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,
}),
];
diff --git a/packages/manager/cypress/e2e/core/objectStorage/object-storage-summary-page.spec.ts b/packages/manager/cypress/e2e/core/objectStorage/object-storage-summary-page.spec.ts
index af828a4a796..49758107569 100644
--- a/packages/manager/cypress/e2e/core/objectStorage/object-storage-summary-page.spec.ts
+++ b/packages/manager/cypress/e2e/core/objectStorage/object-storage-summary-page.spec.ts
@@ -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';
@@ -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({
@@ -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({
@@ -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,
}),
];
diff --git a/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.test.tsx b/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.test.tsx
index 1eb39ebc1bc..51c230bdc35 100644
--- a/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.test.tsx
+++ b/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.test.tsx
@@ -1,3 +1,4 @@
+import { QuotaResourceMetrics } from '@linode/api-v4';
import React from 'react';
import { renderWithTheme } from 'src/utilities/testHelpers';
@@ -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(
-
+
);
const quotaUsageText = getByText(expectedText);
expect(quotaUsageText).toBeVisible();
diff --git a/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.tsx b/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.tsx
index 76228724299..e5acc41f6cc 100644
--- a/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.tsx
+++ b/packages/manager/src/components/QuotaUsageBar/QuotaUsageBar.tsx
@@ -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;
}
@@ -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,
});
diff --git a/packages/manager/src/factories/quotas.ts b/packages/manager/src/factories/quotas.ts
index d3be1d0f7bd..50d2d2dbc3d 100644
--- a/packages/manager/src/factories/quotas.ts
+++ b/packages/manager/src/factories/quotas.ts
@@ -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';
@@ -9,7 +10,7 @@ export const quotaFactory = Factory.Sync.makeFactory({
quota_name: 'Linode Dedicated vCPUs',
quota_type: 'linode-dedicated-cpus',
region_applied: 'us-east',
- resource_metric: 'CPU',
+ resource_metric: QuotaResourceMetrics.CPU,
has_usage: true,
});
diff --git a/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTable.test.tsx b/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTable.test.tsx
index 85b5c7da24f..7b632367ace 100644
--- a/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTable.test.tsx
+++ b/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTable.test.tsx
@@ -1,3 +1,4 @@
+import { QuotaResourceMetrics } from '@linode/api-v4';
import { waitFor } from '@testing-library/react';
import * as React from 'react';
@@ -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,
}),
],
diff --git a/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTableRow.tsx b/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTableRow.tsx
index f9e638533ec..be75ef10bb5 100644
--- a/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTableRow.tsx
+++ b/packages/manager/src/features/Account/Quotas/QuotasTable/QuotasTableRow.tsx
@@ -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';
@@ -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,
});
diff --git a/packages/manager/src/features/Account/Quotas/utils.test.tsx b/packages/manager/src/features/Account/Quotas/utils.test.tsx
index 0015e35cbe3..d4ad5f3d3de 100644
--- a/packages/manager/src/features/Account/Quotas/utils.test.tsx
+++ b/packages/manager/src/features/Account/Quotas/utils.test.tsx
@@ -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';
@@ -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;
@@ -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);
diff --git a/packages/manager/src/features/Account/Quotas/utils.ts b/packages/manager/src/features/Account/Quotas/utils.ts
index 4c926cfbabe..dad407d1df8 100644
--- a/packages/manager/src/features/Account/Quotas/utils.ts
+++ b/packages/manager/src/features/Account/Quotas/utils.ts
@@ -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';
@@ -217,7 +218,7 @@ export const getQuotaIncreaseMessage = ({
interface ConvertResourceMetricProps {
initialLimit: number;
- initialResourceMetric: string;
+ initialResourceMetric: QuotaResourceMetrics;
initialUsage: number;
}
@@ -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),
- };
};
/**
@@ -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) =>
diff --git a/packages/manager/src/features/ObjectStorage/SummaryLanding/Partials/EndpointSummaryRow.test.tsx b/packages/manager/src/features/ObjectStorage/SummaryLanding/Partials/EndpointSummaryRow.test.tsx
index ce5e370de29..dfa16bde5f8 100644
--- a/packages/manager/src/features/ObjectStorage/SummaryLanding/Partials/EndpointSummaryRow.test.tsx
+++ b/packages/manager/src/features/ObjectStorage/SummaryLanding/Partials/EndpointSummaryRow.test.tsx
@@ -1,3 +1,4 @@
+import { QuotaResourceMetrics } from '@linode/api-v4';
import * as React from 'react';
import { quotaFactory, quotaUsageFactory } from 'src/factories/quotas';
@@ -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}`,
@@ -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}`,
@@ -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,
}),
];
diff --git a/packages/manager/src/mocks/presets/crud/handlers/quotas.ts b/packages/manager/src/mocks/presets/crud/handlers/quotas.ts
index 7bbcd0ce3b8..b6d3b8510c3 100644
--- a/packages/manager/src/mocks/presets/crud/handlers/quotas.ts
+++ b/packages/manager/src/mocks/presets/crud/handlers/quotas.ts
@@ -1,3 +1,4 @@
+import { QuotaResourceMetrics } from '@linode/api-v4';
import { pickRandom, regions } from '@linode/utilities';
import { http } from 'msw';
@@ -30,7 +31,7 @@ const mockQuotas: Record = {
quota_limit: 50,
quota_name: 'Dedicated CPU',
region_applied: region.id,
- resource_metric: 'CPU',
+ resource_metric: QuotaResourceMetrics.CPU,
})
),
...regions.map((region) =>
@@ -40,7 +41,7 @@ const mockQuotas: Record = {
quota_limit: 100,
quota_name: 'Shared CPU',
region_applied: region.id,
- resource_metric: 'CPU',
+ resource_metric: QuotaResourceMetrics.CPU,
})
),
...regions.map((region) =>
@@ -49,7 +50,7 @@ const mockQuotas: Record = {
quota_limit: 25,
quota_name: 'GPU',
region_applied: region.id,
- resource_metric: 'GPU',
+ resource_metric: QuotaResourceMetrics.GPU,
})
),
...regions.map((region) =>
@@ -58,7 +59,7 @@ const mockQuotas: Record = {
quota_limit: 10,
quota_name: 'VPU',
region_applied: region.id,
- resource_metric: 'VPU',
+ resource_metric: QuotaResourceMetrics.VPU,
})
),
...regions.map((region) =>
@@ -68,7 +69,7 @@ const mockQuotas: Record = {
quota_limit: 15,
quota_name: 'High Memory',
region_applied: region.id,
- resource_metric: 'CPU',
+ resource_metric: QuotaResourceMetrics.CPU,
})
),
],
@@ -78,7 +79,7 @@ const mockQuotas: Record = {
quota_limit: 50,
quota_name: 'Total number of Clusters',
region_applied: region.id,
- resource_metric: 'cluster',
+ resource_metric: QuotaResourceMetrics.CLUSTER,
})
),
],
@@ -88,7 +89,7 @@ const mockQuotas: Record = {
endpoint_type: 'E0',
quota_limit: 1_000_000_000_000_000, // a petabyte
quota_name: 'Total Capacity',
- resource_metric: 'byte',
+ resource_metric: QuotaResourceMetrics.BYTE,
s3_endpoint: 'us-east-1.linodeobjects.com',
}),
quotaFactory.build({
@@ -97,7 +98,7 @@ const mockQuotas: Record = {
endpoint_type: 'E0',
quota_limit: 100,
quota_name: 'Number of Buckets',
- resource_metric: 'bucket',
+ resource_metric: QuotaResourceMetrics.BUCKET,
s3_endpoint: 'us-west-1.linodeobjects.com',
}),
quotaFactory.build({
@@ -105,7 +106,7 @@ const mockQuotas: Record = {
endpoint_type: 'E3',
quota_limit: 10_000_000,
quota_name: 'Number of Objects',
- resource_metric: 'object',
+ resource_metric: QuotaResourceMetrics.OBJECT,
s3_endpoint: 'br-gru-1.linodeobjects.com',
}),
],
diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts
index fb44b19121c..4d8ce71f581 100644
--- a/packages/manager/src/mocks/serverHandlers.ts
+++ b/packages/manager/src/mocks/serverHandlers.ts
@@ -149,6 +149,8 @@ import type { PathParams } from 'msw';
const getRandomWholeNumber = (min: number, max: number) =>
Math.floor(Math.random() * (max - min + 1) + min);
+import { QuotaResourceMetrics } from '@linode/api-v4';
+
import { accountEntityFactory } from 'src/factories/accountEntities';
import { accountRolesFactory } from 'src/factories/accountRoles';
import { trustedDeviceFactory } from 'src/factories/devices';
@@ -1813,7 +1815,7 @@ export const handlers = [
endpoint_type: 'E0',
quota_limit: 1_000_000_000_000_000,
quota_name: 'Total Capacity',
- resource_metric: 'byte',
+ resource_metric: QuotaResourceMetrics.BYTE,
s3_endpoint: 'endpoint1',
}),
];