Skip to content

Commit

Permalink
feat: UI limit for API tokens (#7532)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
thomasheartman authored Jul 3, 2024
1 parent 08533d7 commit c5fdaea
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,41 @@ import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton
import { CREATE_API_TOKEN_BUTTON } from 'utils/testIds';
import { useNavigate } from 'react-router-dom';
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 {
path: string;
permission: string | 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 = ({
path,
permission,
project,
}: ICreateApiTokenButton) => {
const navigate = useNavigate();
const { tokens, loading } = useApiTokens();
const { uiConfig } = useUiConfig();

const { limitReached, limitMessage } = useApiTokenLimit(
uiConfig.resourceLimits.apiTokens,
tokens.length,
);

return (
<ResponsiveButton
Expand All @@ -23,6 +46,10 @@ export const CreateApiTokenButton = ({
permission={permission}
projectId={project}
maxWidth='700px'
disabled={loading || limitReached}
tooltipProps={{
title: limitMessage,
}}
>
New API token
</ResponsiveButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ export const defaultValue: IUiConfig = {
constraintValues: 250,
projects: 500,
segments: 300,
apiTokens: 2000,
},
};
9 changes: 8 additions & 1 deletion frontend/src/openapi/models/resourceLimitsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export interface ResourceLimitsSchema {
constraintValues: number;
/** The maximum number of projects allowed. */
projects: number;
/** The maximum number of segment allowed. */
/** The maximum number of segments allowed. */
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;
}

0 comments on commit c5fdaea

Please sign in to comment.