1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-28 00:06:53 +01:00

feat: UI limit for API tokens (#7532)

This PR activates the limit for API token creation in both the global
API token window and in the project-level API token tab.

Because the same button is used in two places, I encapsulated the
fetching of flags and resource limits within the button. I can be
convinced to pass the current API token count and the limit as
arguments, but I think this is the right solution for this case.
This commit is contained in:
Thomas Heartman 2024-07-03 14:36:48 +02:00 committed by GitHub
parent 08533d7224
commit c5fdaeabd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 103 additions and 1 deletions

View File

@ -0,0 +1,67 @@
import { screen, waitFor } from '@testing-library/react';
import { render } from 'utils/testRenderer';
import { testServerRoute, testServerSetup } from 'utils/testServer';
import { CreateApiTokenButton } from './CreateApiTokenButton';
import { CREATE_PROJECT_API_TOKEN } from 'component/providers/AccessProvider/permissions';
const server = testServerSetup();
const setupApi = ({
apiTokenCount,
apiTokenLimit,
}: { apiTokenCount: number; apiTokenLimit: number }) => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
resourceLimits: true,
},
resourceLimits: {
apiTokens: apiTokenLimit,
},
});
testServerRoute(server, '/api/admin/api-tokens', {
tokens: Array.from({ length: apiTokenCount }).map((_, i) => ({
secret: 'super-secret',
tokenName: `token—name-${i}`,
type: 'client',
})),
});
};
test('should allow you to create API tokens when there are fewer apiTokens than the limit', async () => {
setupApi({ apiTokenLimit: 3, apiTokenCount: 2 });
render(
<CreateApiTokenButton
permission={CREATE_PROJECT_API_TOKEN}
path='create'
/>,
{
permissions: [{ permission: CREATE_PROJECT_API_TOKEN }],
},
);
await waitFor(async () => {
const button = await screen.findByRole('button');
expect(button).not.toBeDisabled();
});
});
test('should not allow you to create API tokens when you have reached the limit', async () => {
setupApi({ apiTokenLimit: 3, apiTokenCount: 3 });
render(
<CreateApiTokenButton
permission={CREATE_PROJECT_API_TOKEN}
path='create'
/>,
{
permissions: [{ permission: CREATE_PROJECT_API_TOKEN }],
},
);
await waitFor(async () => {
const button = await screen.findByRole('button');
expect(button).toBeDisabled();
});
});

View File

@ -2,18 +2,41 @@ import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton
import { CREATE_API_TOKEN_BUTTON } from 'utils/testIds'; import { CREATE_API_TOKEN_BUTTON } from 'utils/testIds';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Add from '@mui/icons-material/Add'; import Add from '@mui/icons-material/Add';
import { useApiTokens } from 'hooks/api/getters/useApiTokens/useApiTokens';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
interface ICreateApiTokenButton { interface ICreateApiTokenButton {
path: string; path: string;
permission: string | string[]; permission: string | string[];
project?: string; project?: string;
} }
const useApiTokenLimit = (apiTokenLimit: number, apiTokenCount: number) => {
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const limitReached =
resourceLimitsEnabled && apiTokenCount >= apiTokenLimit;
return {
limitReached,
limitMessage: limitReached
? `You have reached the limit of ${apiTokenLimit} API tokens`
: undefined,
};
};
export const CreateApiTokenButton = ({ export const CreateApiTokenButton = ({
path, path,
permission, permission,
project, project,
}: ICreateApiTokenButton) => { }: ICreateApiTokenButton) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { tokens, loading } = useApiTokens();
const { uiConfig } = useUiConfig();
const { limitReached, limitMessage } = useApiTokenLimit(
uiConfig.resourceLimits.apiTokens,
tokens.length,
);
return ( return (
<ResponsiveButton <ResponsiveButton
@ -23,6 +46,10 @@ export const CreateApiTokenButton = ({
permission={permission} permission={permission}
projectId={project} projectId={project}
maxWidth='700px' maxWidth='700px'
disabled={loading || limitReached}
tooltipProps={{
title: limitMessage,
}}
> >
New API token New API token
</ResponsiveButton> </ResponsiveButton>

View File

@ -42,5 +42,6 @@ export const defaultValue: IUiConfig = {
constraintValues: 250, constraintValues: 250,
projects: 500, projects: 500,
segments: 300, segments: 300,
apiTokens: 2000,
}, },
}; };

View File

@ -32,6 +32,13 @@ export interface ResourceLimitsSchema {
constraintValues: number; constraintValues: number;
/** The maximum number of projects allowed. */ /** The maximum number of projects allowed. */
projects: number; projects: number;
/** The maximum number of segment allowed. */ /** The maximum number of segments allowed. */
segments: number; segments: number;
/** The maximum number of SDK and admin API tokens you can have at
* the same time. This limit applies only to server-side and
* client-side SDK tokens and to admin tokens. Personal access
* tokens are not subject to this limit. The limit applies to the
* total number of tokens across all projects in your
* organization. */
apiTokens: number;
} }