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:
parent
08533d7224
commit
c5fdaeabd9
@ -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();
|
||||||
|
});
|
||||||
|
});
|
@ -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>
|
||||||
|
@ -42,5 +42,6 @@ export const defaultValue: IUiConfig = {
|
|||||||
constraintValues: 250,
|
constraintValues: 250,
|
||||||
projects: 500,
|
projects: 500,
|
||||||
segments: 300,
|
segments: 300,
|
||||||
|
apiTokens: 2000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user