1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

[Gitar] Cleaning up stale flag: resourceLimits with value true (#7964)

[![Gitar](https://raw.githubusercontent.com/gitarcode/.github/main/assets/gitar-banner.svg)](https://gitar.co)
  
  ---
This automated PR was generated by [Gitar](https://gitar.co). View
[docs](https://gitar.co/docs).

---------

Co-authored-by: Gitar <noreply@gitar.co>
Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
gitar-bot[bot] 2024-08-22 13:20:53 +02:00 committed by GitHub
parent b0541a0af2
commit 4615ff40ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 177 additions and 392 deletions

View File

@ -18,9 +18,6 @@ const server = testServerSetup();
const setupApi = (existingTokensCount: number) => { const setupApi = (existingTokensCount: number) => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: {
resourceLimits: true,
},
resourceLimits: { resourceLimits: {
apiTokens: 1, apiTokens: 1,
}, },

View File

@ -1,8 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { styled } from '@mui/material'; import { styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useUiFlag } from 'hooks/useUiFlag';
import FormTemplate from 'component/common/FormTemplate/FormTemplate'; import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import ApiTokenForm from '../ApiTokenForm/ApiTokenForm'; import ApiTokenForm from '../ApiTokenForm/ApiTokenForm';
import { CreateButton } from 'component/common/CreateButton/CreateButton'; import { CreateButton } from 'component/common/CreateButton/CreateButton';
@ -37,16 +35,14 @@ const StyledLimit = styled(Limit)(({ theme }) => ({
})); }));
const useApiTokenLimit = () => { const useApiTokenLimit = () => {
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { tokens, loading: loadingTokens } = useApiTokens(); const { tokens, loading: loadingTokens } = useApiTokens();
const { uiConfig, loading: loadingConfig } = useUiConfig(); const { uiConfig, loading: loadingConfig } = useUiConfig();
const apiTokensLimit = uiConfig.resourceLimits.apiTokens; const apiTokensLimit = uiConfig.resourceLimits.apiTokens;
return { return {
resourceLimitsEnabled,
limit: apiTokensLimit, limit: apiTokensLimit,
currentValue: tokens.length, currentValue: tokens.length,
limitReached: resourceLimitsEnabled && tokens.length >= apiTokensLimit, limitReached: tokens.length >= apiTokensLimit,
loading: loadingConfig || loadingTokens, loading: loadingConfig || loadingTokens,
}; };
}; };
@ -58,7 +54,6 @@ export const CreateApiToken = ({ modal = false }: ICreateApiTokenProps) => {
const [showConfirm, setShowConfirm] = useState(false); const [showConfirm, setShowConfirm] = useState(false);
const [token, setToken] = useState(''); const [token, setToken] = useState('');
const { const {
resourceLimitsEnabled,
limit, limit,
currentValue, currentValue,
limitReached, limitReached,
@ -176,16 +171,11 @@ export const CreateApiToken = ({ modal = false }: ICreateApiTokenProps) => {
environment={environment} environment={environment}
setEnvironment={setEnvironment} setEnvironment={setEnvironment}
/> />
<ConditionallyRender <StyledLimit
condition={resourceLimitsEnabled} name='API tokens'
show={ shortName='tokens'
<StyledLimit currentValue={currentValue}
name='API tokens' limit={limit}
shortName='tokens'
currentValue={currentValue}
limit={limit}
/>
}
/> />
</ApiTokenForm> </ApiTokenForm>
<ConfirmToken <ConfirmToken

View File

@ -11,12 +11,7 @@ const setupApi = ({
apiTokenLimit, apiTokenLimit,
}: { apiTokenCount: number; apiTokenLimit: number }) => { }: { apiTokenCount: number; apiTokenLimit: number }) => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: { resourceLimits: { apiTokens: apiTokenLimit },
resourceLimits: true,
},
resourceLimits: {
apiTokens: apiTokenLimit,
},
}); });
testServerRoute(server, '/api/admin/api-tokens', { testServerRoute(server, '/api/admin/api-tokens', {

View File

@ -3,7 +3,6 @@ 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 { useApiTokens } from 'hooks/api/getters/useApiTokens/useApiTokens';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
interface ICreateApiTokenButton { interface ICreateApiTokenButton {
path: string; path: string;
@ -12,9 +11,7 @@ interface ICreateApiTokenButton {
} }
const useApiTokenLimit = (apiTokenLimit: number, apiTokenCount: number) => { const useApiTokenLimit = (apiTokenLimit: number, apiTokenCount: number) => {
const resourceLimitsEnabled = useUiFlag('resourceLimits'); const limitReached = apiTokenCount >= apiTokenLimit;
const limitReached =
resourceLimitsEnabled && apiTokenCount >= apiTokenLimit;
return { return {
limitReached, limitReached,

View File

@ -6,15 +6,9 @@ import { FreeTextInput } from './FreeTextInput';
const server = testServerSetup(); const server = testServerSetup();
const LIMIT = 3; const LIMIT = 3;
const setupApi = () => { const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: { resourceLimits: { constraintValues: LIMIT },
resourceLimits: true,
},
resourceLimits: {
constraintValues: LIMIT,
},
}); });
}; };
@ -126,8 +120,8 @@ test('should show limit reached indicator', async () => {
<FreeTextInput <FreeTextInput
error='' error=''
values={['1', '2', '3']} values={['1', '2', '3']}
setValues={(newValues) => {}} setValues={() => {}}
setError={(newError: string) => {}} setError={() => {}}
removeValue={() => {}} removeValue={() => {}}
/>, />,
); );

View File

@ -6,9 +6,7 @@ import type React from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader'; import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader';
import { parseParameterStrings } from 'utils/parseParameter'; import { parseParameterStrings } from 'utils/parseParameter';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
interface IFreeTextInputProps { interface IFreeTextInputProps {
@ -72,7 +70,6 @@ export const FreeTextInput = ({
}: IFreeTextInputProps) => { }: IFreeTextInputProps) => {
const [inputValues, setInputValues] = useState(''); const [inputValues, setInputValues] = useState('');
const { classes: styles } = useStyles(); const { classes: styles } = useStyles();
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { uiConfig, loading } = useUiConfig(); const { uiConfig, loading } = useUiConfig();
const constraintValuesLimit = uiConfig.resourceLimits.constraintValues; const constraintValuesLimit = uiConfig.resourceLimits.constraintValues;
@ -88,9 +85,7 @@ export const FreeTextInput = ({
...values, ...values,
...parseParameterStrings(inputValues), ...parseParameterStrings(inputValues),
]); ]);
const limitReached = Boolean( const limitReached = Boolean(newValues.length > constraintValuesLimit);
resourceLimitsEnabled && newValues.length > constraintValuesLimit,
);
if (limitReached) { if (limitReached) {
setError( setError(
@ -148,16 +143,11 @@ export const FreeTextInput = ({
/> />
</div> </div>
<LimitContainer> <LimitContainer>
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='single constraint values'
show={ shortName='values'
<Limit currentValue={values.length}
name='single constraint values' limit={constraintValuesLimit}
shortName='values'
currentValue={values.length}
limit={constraintValuesLimit}
/>
}
/> />
</LimitContainer> </LimitContainer>
</> </>

View File

@ -7,14 +7,10 @@ import { ADMIN } from '../../providers/AccessProvider/permissions';
const server = testServerSetup(); const server = testServerSetup();
const setupApi = ({ const setupApi = ({
resourceLimits,
limit, limit,
environments, environments,
}: { resourceLimits: boolean; limit: number; environments: number }) => { }: { limit: number; environments: number }) => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: {
resourceLimits,
},
resourceLimits: { resourceLimits: {
environments: limit, environments: limit,
}, },
@ -28,7 +24,7 @@ const setupApi = ({
}; };
test('show limit reached info', async () => { test('show limit reached info', async () => {
setupApi({ environments: 1, limit: 1, resourceLimits: true }); setupApi({ environments: 1, limit: 1 });
render(<CreateEnvironment />, { permissions: [{ permission: ADMIN }] }); render(<CreateEnvironment />, { permissions: [{ permission: ADMIN }] });
await screen.findByText('You have reached the limit for environments'); await screen.findByText('You have reached the limit for environments');
@ -39,7 +35,7 @@ test('show limit reached info', async () => {
}); });
test('show approaching limit info', async () => { test('show approaching limit info', async () => {
setupApi({ environments: 9, limit: 10, resourceLimits: true }); setupApi({ environments: 9, limit: 10 });
render(<CreateEnvironment />, { permissions: [{ permission: ADMIN }] }); render(<CreateEnvironment />, { permissions: [{ permission: ADMIN }] });
await screen.findByText('You are nearing the limit for environments'); await screen.findByText('You are nearing the limit for environments');
@ -48,10 +44,3 @@ test('show approaching limit info', async () => {
}); });
expect(createButton).toBeEnabled(); expect(createButton).toBeEnabled();
}); });
test('show limit reached info - no resource limits component', async () => {
setupApi({ environments: 1, limit: 1, resourceLimits: false });
render(<CreateEnvironment />);
await screen.findByText('Go back');
});

View File

@ -2,27 +2,20 @@ import { useNavigate } from 'react-router-dom';
import useEnvironmentForm from '../hooks/useEnvironmentForm'; import useEnvironmentForm from '../hooks/useEnvironmentForm';
import EnvironmentForm from '../EnvironmentForm/EnvironmentForm'; import EnvironmentForm from '../EnvironmentForm/EnvironmentForm';
import FormTemplate from 'component/common/FormTemplate/FormTemplate'; import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import { Alert } from '@mui/material';
import { Button } from '@mui/material';
import { CreateButton } from 'component/common/CreateButton/CreateButton'; import { CreateButton } from 'component/common/CreateButton/CreateButton';
import useEnvironmentApi from 'hooks/api/actions/useEnvironmentApi/useEnvironmentApi'; import useEnvironmentApi from 'hooks/api/actions/useEnvironmentApi/useEnvironmentApi';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
import usePermissions from 'hooks/api/getters/usePermissions/usePermissions'; import usePermissions from 'hooks/api/getters/usePermissions/usePermissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { PageContent } from 'component/common/PageContent/PageContent';
import { ADMIN } from 'component/providers/AccessProvider/permissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { GO_BACK } from 'constants/navigate'; import { GO_BACK } from 'constants/navigate';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
import { useUiFlag } from 'hooks/useUiFlag';
const CreateEnvironment = () => { const CreateEnvironment = () => {
const { setToastApiError, setToastData } = useToast(); const { setToastApiError, setToastData } = useToast();
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const environmentLimit = uiConfig.resourceLimits.environments; const environmentLimit = uiConfig.resourceLimits.environments;
const navigate = useNavigate(); const navigate = useNavigate();
const { environments } = useEnvironments(); const { environments } = useEnvironments();
@ -73,13 +66,10 @@ const CreateEnvironment = () => {
}; };
return ( return (
<ConditionallyRender <FormTemplate
condition={resourceLimitsEnabled || canCreateMoreEnvs} loading={loading}
show={ title='Create environment'
<FormTemplate description='Environments allow you to manage your
loading={loading}
title='Create environment'
description='Environments allow you to manage your
product lifecycle from local development product lifecycle from local development
through production. Your projects and through production. Your projects and
feature flags are accessible in all your feature flags are accessible in all your
@ -89,66 +79,36 @@ const CreateEnvironment = () => {
development or test environment without development or test environment without
enabling the feature flag in the enabling the feature flag in the
production environment.' production environment.'
documentationLink='https://docs.getunleash.io/reference/environments' documentationLink='https://docs.getunleash.io/reference/environments'
documentationLinkLabel='Environments documentation' documentationLinkLabel='Environments documentation'
formatApiCode={formatApiCode} formatApiCode={formatApiCode}
> >
<EnvironmentForm <EnvironmentForm
errors={errors} errors={errors}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
handleCancel={handleCancel} handleCancel={handleCancel}
validateEnvironmentName={validateEnvironmentName} validateEnvironmentName={validateEnvironmentName}
name={name} name={name}
type={type} type={type}
setName={setName} setName={setName}
setType={setType} setType={setType}
mode='Create' mode='Create'
clearErrors={clearErrors} clearErrors={clearErrors}
Limit={ Limit={
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='environments'
show={ limit={environmentLimit}
<Limit currentValue={environments.length}
name='environments' />
limit={environmentLimit} }
currentValue={environments.length} >
/> <CreateButton
} name='environment'
/> permission={ADMIN}
} disabled={!canCreateMoreEnvs}
> />
<CreateButton </EnvironmentForm>
name='environment' </FormTemplate>
permission={ADMIN}
disabled={!canCreateMoreEnvs}
/>
</EnvironmentForm>
</FormTemplate>
}
elseShow={
<>
<PageContent
header={<PageHeader title='Create environment' />}
>
<Alert severity='error'>
<p>
Currently Unleash does not support more than{' '}
{environmentLimit} environments. If you need
more please reach out.
</p>
</Alert>
<br />
<Button
onClick={handleCancel}
variant='contained'
color='primary'
>
Go back
</Button>
</PageContent>
</>
}
/>
); );
}; };

View File

@ -10,9 +10,6 @@ const LIMIT = 5;
const setupApi = () => { const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: {
resourceLimits: true,
},
resourceLimits: { resourceLimits: {
constraints: LIMIT, constraints: LIMIT,
}, },

View File

@ -1,8 +1,7 @@
import type React from 'react'; import type React from 'react';
import { forwardRef, type RefObject } from 'react'; import { forwardRef, type RefObject } from 'react';
import { Box, Button, styled, Tooltip, Typography } from '@mui/material'; import { Box, Button, styled, Typography } from '@mui/material';
import Add from '@mui/icons-material/Add'; import Add from '@mui/icons-material/Add';
import HelpOutline from '@mui/icons-material/HelpOutline';
import type { IConstraint } from 'interfaces/strategy'; import type { IConstraint } from 'interfaces/strategy';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -13,7 +12,6 @@ import {
} from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
import { NewConstraintAccordionList } from 'component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList'; import { NewConstraintAccordionList } from 'component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
interface IConstraintAccordionListProps { interface IConstraintAccordionListProps {
@ -32,30 +30,6 @@ const StyledContainer = styled('div')({
flexDirection: 'column', flexDirection: 'column',
}); });
const StyledHelpWrapper = styled(Tooltip)(({ theme }) => ({
marginLeft: theme.spacing(0.75),
height: theme.spacing(1.5),
}));
const StyledHelp = styled(HelpOutline)(({ theme }) => ({
fill: theme.palette.action.active,
[theme.breakpoints.down(860)]: {
display: 'none',
},
}));
const StyledConstraintLabel = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1),
color: theme.palette.text.secondary,
}));
const StyledAddCustomLabel = styled('div')(({ theme }) => ({
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
color: theme.palette.text.primary,
display: 'flex',
}));
const StyledHelpIconBox = styled(Box)(({ theme }) => ({ const StyledHelpIconBox = styled(Box)(({ theme }) => ({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@ -64,14 +38,11 @@ const StyledHelpIconBox = styled(Box)(({ theme }) => ({
})); }));
const useConstraintLimit = (constraintsCount: number) => { const useConstraintLimit = (constraintsCount: number) => {
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const constraintsLimit = uiConfig.resourceLimits?.constraints || 30; const constraintsLimit = uiConfig.resourceLimits?.constraints || 30;
const limitReached = const limitReached = constraintsCount >= constraintsLimit;
resourceLimitsEnabled && constraintsCount >= constraintsLimit;
return { return {
resourceLimitsEnabled,
limit: constraintsLimit, limit: constraintsLimit,
limitReached, limitReached,
}; };
@ -80,93 +51,81 @@ const useConstraintLimit = (constraintsCount: number) => {
export const FeatureStrategyConstraintAccordionList = forwardRef< export const FeatureStrategyConstraintAccordionList = forwardRef<
IConstraintAccordionListRef | undefined, IConstraintAccordionListRef | undefined,
IConstraintAccordionListProps IConstraintAccordionListProps
>( >(({ constraints, setConstraints, showCreateButton }, ref) => {
( const { onAdd, state, context } = useConstraintAccordionList(
{ constraints, setConstraints, showCreateButton, showLabel = true }, setConstraints,
ref, ref as RefObject<IConstraintAccordionListRef>,
) => { );
const { onAdd, state, context } = useConstraintAccordionList( const { limit, limitReached } = useConstraintLimit(constraints.length);
setConstraints,
ref as RefObject<IConstraintAccordionListRef>,
);
const { resourceLimitsEnabled, limit, limitReached } =
useConstraintLimit(constraints.length);
if (context.length === 0) { if (context.length === 0) {
return null; return null;
} }
return ( return (
<StyledContainer id={constraintAccordionListId}> <StyledContainer id={constraintAccordionListId}>
<ConditionallyRender <ConditionallyRender
condition={Boolean(showCreateButton && onAdd)} condition={Boolean(showCreateButton && onAdd)}
show={ show={
<div> <div>
<StyledHelpIconBox> <StyledHelpIconBox>
<Typography>Constraints</Typography> <Typography>Constraints</Typography>
<HelpIcon <HelpIcon
htmlTooltip htmlTooltip
tooltip={ tooltip={
<Box> <Box>
<Typography variant='body2'> <Typography variant='body2'>
Constraints are advanced Constraints are advanced targeting
targeting rules that you can use rules that you can use to enable a
to enable a feature flag for a feature flag for a subset of your
subset of your users. Read more users. Read more about constraints{' '}
about constraints{' '} <a
<a href='https://docs.getunleash.io/reference/strategy-constraints'
href='https://docs.getunleash.io/reference/strategy-constraints' target='_blank'
target='_blank' rel='noopener noreferrer'
rel='noopener noreferrer' >
> here
here </a>
</a> </Typography>
</Typography> </Box>
</Box> }
}
/>
</StyledHelpIconBox>
<NewConstraintAccordionList
ref={ref}
setConstraints={setConstraints}
constraints={constraints}
state={state}
/> />
</StyledHelpIconBox>
<NewConstraintAccordionList
ref={ref}
setConstraints={setConstraints}
constraints={constraints}
state={state}
/>
<Box <Box
sx={(theme) => ({ sx={(theme) => ({
marginTop: theme.spacing(2), marginTop: theme.spacing(2),
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
})} })}
> >
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='constraints in this strategy'
show={ shortName='constraints'
<Limit currentValue={constraints.length}
name='constraints in this strategy' limit={limit}
shortName='constraints' />
currentValue={constraints.length} </Box>
limit={limit}
/>
}
/>
</Box>
<Button <Button
type='button' type='button'
onClick={onAdd} onClick={onAdd}
startIcon={<Add />} startIcon={<Add />}
variant='outlined' variant='outlined'
color='primary' color='primary'
data-testid='ADD_CONSTRAINT_BUTTON' data-testid='ADD_CONSTRAINT_BUTTON'
disabled={Boolean(limitReached)} disabled={Boolean(limitReached)}
> >
Add constraint Add constraint
</Button> </Button>
</div> </div>
} }
/> />
</StyledContainer> </StyledContainer>
); );
}, });
);

View File

@ -36,21 +36,15 @@ import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { useDefaultStrategy } from '../../../project/Project/ProjectSettings/ProjectDefaultStrategySettings/ProjectEnvironment/ProjectEnvironmentDefaultStrategy/EditDefaultStrategy'; import { useDefaultStrategy } from '../../../project/Project/ProjectSettings/ProjectDefaultStrategySettings/ProjectEnvironment/ProjectEnvironmentDefaultStrategy/EditDefaultStrategy';
import { FeatureStrategyForm } from '../FeatureStrategyForm/FeatureStrategyForm'; import { FeatureStrategyForm } from '../FeatureStrategyForm/FeatureStrategyForm';
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants'; import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
import { useUiFlag } from 'hooks/useUiFlag';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const useStrategyLimit = (strategyCount: number) => { const useStrategyLimit = (strategyCount: number) => {
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const featureEnvironmentStrategiesLimit = const featureEnvironmentStrategiesLimit =
uiConfig.resourceLimits?.featureEnvironmentStrategies || 100; uiConfig.resourceLimits?.featureEnvironmentStrategies || 100;
const limitReached = const limitReached = strategyCount >= featureEnvironmentStrategiesLimit;
resourceLimitsEnabled &&
strategyCount >= featureEnvironmentStrategiesLimit;
return { return {
resourceLimitsEnabled,
limit: featureEnvironmentStrategiesLimit, limit: featureEnvironmentStrategiesLimit,
limitReached, limitReached,
}; };
@ -93,8 +87,7 @@ export const FeatureStrategyCreate = () => {
(featureEnvironment) => featureEnvironment.name === environmentId, (featureEnvironment) => featureEnvironment.name === environmentId,
); );
const strategyCount = featureEnvironment?.strategies.length || 0; const strategyCount = featureEnvironment?.strategies.length || 0;
const { limit, limitReached, resourceLimitsEnabled } = const { limit, limitReached } = useStrategyLimit(strategyCount);
useStrategyLimit(strategyCount);
const ref = useRef<IFeatureToggle>(feature); const ref = useRef<IFeatureToggle>(feature);
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const { refetch: refetchChangeRequests } = const { refetch: refetchChangeRequests } =
@ -247,16 +240,11 @@ export const FeatureStrategyCreate = () => {
/> />
} }
Limit={ Limit={
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='strategies in this environment'
show={ shortName='strategies'
<Limit currentValue={strategyCount}
name='strategies in this environment' limit={limit}
shortName='strategies'
currentValue={strategyCount}
limit={limit}
/>
}
/> />
} }
disabled={limitReached} disabled={limitReached}

View File

@ -86,7 +86,6 @@ export const setupUiConfigEndpoint = () => {
environment: 'enterprise', environment: 'enterprise',
flags: { flags: {
newStrategyConfiguration: true, newStrategyConfiguration: true,
resourceLimits: true,
}, },
resourceLimits: { resourceLimits: {
featureEnvironmentStrategies: 2, featureEnvironmentStrategies: 2,

View File

@ -8,12 +8,7 @@ const server = testServerSetup();
const setupApi = (existingProjectsCount: number) => { const setupApi = (existingProjectsCount: number) => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: { resourceLimits: { projects: 1 },
resourceLimits: true,
},
resourceLimits: {
projects: 1,
},
versionInfo: { versionInfo: {
current: { enterprise: 'version' }, current: { enterprise: 'version' },
}, },

View File

@ -16,7 +16,6 @@ import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Dialog, styled } from '@mui/material'; import { Dialog, styled } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag';
import useProjects from 'hooks/api/getters/useProjects/useProjects'; import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -96,15 +95,12 @@ const configButtonData = {
}; };
const useProjectLimit = () => { const useProjectLimit = () => {
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { projects, loading: loadingProjects } = useProjects(); const { projects, loading: loadingProjects } = useProjects();
const { uiConfig, loading: loadingConfig } = useUiConfig(); const { uiConfig, loading: loadingConfig } = useUiConfig();
const projectsLimit = uiConfig.resourceLimits?.projects; const projectsLimit = uiConfig.resourceLimits?.projects;
const limitReached = const limitReached = projects.length >= projectsLimit;
resourceLimitsEnabled && projects.length >= projectsLimit;
return { return {
resourceLimitsEnabled,
limit: projectsLimit, limit: projectsLimit,
currentValue: projects.length, currentValue: projects.length,
limitReached, limitReached,
@ -201,7 +197,6 @@ export const CreateProjectDialog = ({
}; };
const { const {
resourceLimitsEnabled,
limit, limit,
currentValue, currentValue,
limitReached, limitReached,
@ -251,15 +246,10 @@ export const CreateProjectDialog = ({
creatingProject || limitReached || loadingLimit, creatingProject || limitReached || loadingLimit,
}} }}
Limit={ Limit={
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='projects'
show={ limit={limit}
<Limit currentValue={currentValue}
name='projects'
limit={limit}
currentValue={currentValue}
/>
}
/> />
} }
handleSubmit={handleSubmit} handleSubmit={handleSubmit}

View File

@ -12,7 +12,6 @@ import {
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Dialog, styled } from '@mui/material'; import { Dialog, styled } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag';
import useProjects from 'hooks/api/getters/useProjects/useProjects'; import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { Limit } from 'component/common/Limit/Limit'; import { Limit } from 'component/common/Limit/Limit';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -174,8 +173,6 @@ const CreateFeatureDialogContent = ({
const { project: projectInfo } = useProjectOverview(project); const { project: projectInfo } = useProjectOverview(project);
const { tags: allTags } = useAllTags(); const { tags: allTags } = useAllTags();
const resourceLimitsEnabled = useUiFlag('resourceLimits');
const { globalFlagLimitReached, projectFlagLimitReached, limitMessage } = const { globalFlagLimitReached, projectFlagLimitReached, limitMessage } =
useFlagLimits({ useFlagLimits({
global: { global: {
@ -233,15 +230,10 @@ const CreateFeatureDialogContent = ({
Icon={<FlagIcon />} Icon={<FlagIcon />}
validateName={validateToggleName} validateName={validateToggleName}
Limit={ Limit={
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='feature flags'
show={ limit={uiConfig.resourceLimits.featureFlags}
<Limit currentValue={totalFlags ?? 0}
name='feature flags'
limit={uiConfig.resourceLimits.featureFlags}
currentValue={totalFlags ?? 0}
/>
}
/> />
} }
name={name} name={name}

View File

@ -1,5 +1,3 @@
import { useUiFlag } from 'hooks/useUiFlag';
type FlagLimitsProps = { type FlagLimitsProps = {
global: { limit: number; count: number }; global: { limit: number; count: number };
project: { limit?: number; count: number }; project: { limit?: number; count: number };
@ -30,8 +28,7 @@ export const useFlagLimits = ({ global, project }: FlagLimitsProps) => {
}; };
const useGlobalFlagLimit = (flagLimit: number, flagCount: number) => { const useGlobalFlagLimit = (flagLimit: number, flagCount: number) => {
const resourceLimitsEnabled = useUiFlag('resourceLimits'); const limitReached = flagCount >= flagLimit;
const limitReached = resourceLimitsEnabled && flagCount >= flagLimit;
return { return {
limitReached, limitReached,

View File

@ -8,12 +8,7 @@ const server = testServerSetup();
const setupApi = () => { const setupApi = () => {
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: { resourceLimits: { projects: 1 },
resourceLimits: true,
},
resourceLimits: {
projects: 1,
},
versionInfo: { versionInfo: {
current: { enterprise: 'version' }, current: { enterprise: 'version' },
}, },

View File

@ -18,7 +18,6 @@ const setupRoutes = ({
testServerRoute(server, '/api/admin/ui-config', { testServerRoute(server, '/api/admin/ui-config', {
flags: { flags: {
SE: true, SE: true,
resourceLimits: true,
}, },
resourceLimits: { resourceLimits: {
segments: limit, segments: limit,

View File

@ -20,7 +20,6 @@ import {
import { SegmentProjectAlert } from './SegmentProjectAlert'; import { SegmentProjectAlert } from './SegmentProjectAlert';
import { sortStrategiesByFeature } from './SegmentDelete/SegmentDeleteUsedSegment/sort-strategies'; import { sortStrategiesByFeature } from './SegmentDelete/SegmentDeleteUsedSegment/sort-strategies';
import type { IFeatureStrategy } from 'interfaces/strategy'; import type { IFeatureStrategy } from 'interfaces/strategy';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { Limit } from '../common/Limit/Limit'; import { Limit } from '../common/Limit/Limit';
@ -37,15 +36,15 @@ interface ISegmentFormPartOneProps {
setCurrentStep: React.Dispatch<React.SetStateAction<SegmentFormStep>>; setCurrentStep: React.Dispatch<React.SetStateAction<SegmentFormStep>>;
} }
const StyledForm = styled('div')(({ theme }) => ({ const StyledForm = styled('div')({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
height: '100%', height: '100%',
})); });
const StyledContainer = styled('div')(({ theme }) => ({ const StyledContainer = styled('div')({
maxWidth: '400px', maxWidth: '400px',
})); });
const StyledInputDescription = styled('p')(({ theme }) => ({ const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1), marginBottom: theme.spacing(1),
@ -56,11 +55,11 @@ const StyledInput = styled(Input)(({ theme }) => ({
marginBottom: theme.spacing(2), marginBottom: theme.spacing(2),
})); }));
const StyledButtonContainer = styled('div')(({ theme }) => ({ const StyledButtonContainer = styled('div')({
marginTop: 'auto', marginTop: 'auto',
display: 'flex', display: 'flex',
justifyContent: 'flex-end', justifyContent: 'flex-end',
})); });
const StyledCancelButton = styled(Button)(({ theme }) => ({ const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3), marginLeft: theme.spacing(3),
@ -79,16 +78,13 @@ const useSegmentLimit = () => {
const { uiConfig, loading: loadingConfig } = useUiConfig(); const { uiConfig, loading: loadingConfig } = useUiConfig();
const segmentsLimit = uiConfig.resourceLimits.segments; const segmentsLimit = uiConfig.resourceLimits.segments;
const segmentsCount = segments?.length || 0; const segmentsCount = segments?.length || 0;
const resourceLimitsEnabled = useUiFlag('resourceLimits'); const limitReached = segmentsCount >= segmentsLimit;
const limitReached =
resourceLimitsEnabled && segmentsCount >= segmentsLimit;
return { return {
limit: segmentsLimit, limit: segmentsLimit,
limitReached, limitReached,
currentCount: segmentsCount, currentCount: segmentsCount,
loading: loadingSegments || loadingConfig, loading: loadingSegments || loadingConfig,
resourceLimitsEnabled,
}; };
}; };
@ -111,7 +107,6 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
limit, limit,
currentCount, currentCount,
loading: loadingSegmentLimit, loading: loadingSegmentLimit,
resourceLimitsEnabled,
} = useSegmentLimit(); } = useSegmentLimit();
const { const {
@ -204,15 +199,10 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
</StyledContainer> </StyledContainer>
<LimitContainer> <LimitContainer>
<ConditionallyRender <Limit
condition={resourceLimitsEnabled} name='segments'
show={ limit={limit}
<Limit currentValue={currentCount}
name='segments'
limit={limit}
currentValue={currentCount}
/>
}
/> />
</LimitContainer> </LimitContainer>

View File

@ -88,7 +88,6 @@ export type UiFlags = {
enableLegacyVariants?: boolean; enableLegacyVariants?: boolean;
navigationSidebar?: boolean; navigationSidebar?: boolean;
flagCreator?: boolean; flagCreator?: boolean;
resourceLimits?: boolean;
newEventSearch?: boolean; newEventSearch?: boolean;
archiveProjects?: boolean; archiveProjects?: boolean;
projectListImprovements?: boolean; projectListImprovements?: boolean;

View File

@ -142,7 +142,6 @@ exports[`should create default config 1`] = `
"projectOverviewRefactorFeedback": false, "projectOverviewRefactorFeedback": false,
"queryMissingTokens": false, "queryMissingTokens": false,
"removeUnsafeInlineStyleSrc": false, "removeUnsafeInlineStyleSrc": false,
"resourceLimits": false,
"responseTimeMetricsFix": false, "responseTimeMetricsFix": false,
"responseTimeWithAppNameKillSwitch": false, "responseTimeWithAppNameKillSwitch": false,
"showInactiveUsers": false, "showInactiveUsers": false,

View File

@ -378,8 +378,6 @@ class FeatureToggleService {
environment: string; environment: string;
featureName: string; featureName: string;
}) { }) {
if (!this.flagResolver.isEnabled('resourceLimits')) return;
const limit = this.resourceLimits.featureEnvironmentStrategies; const limit = this.resourceLimits.featureEnvironmentStrategies;
const existingCount = ( const existingCount = (
await this.featureStrategiesStore.getStrategiesForFeatureEnv( await this.featureStrategiesStore.getStrategiesForFeatureEnv(
@ -400,8 +398,6 @@ class FeatureToggleService {
updated: IConstraint[]; updated: IConstraint[];
existing: IConstraint[]; existing: IConstraint[];
}) { }) {
if (!this.flagResolver.isEnabled('resourceLimits')) return;
const { const {
constraints: constraintsLimit, constraints: constraintsLimit,
constraintValues: constraintValuesLimit, constraintValues: constraintValuesLimit,
@ -1223,15 +1219,13 @@ class FeatureToggleService {
} }
private async validateFeatureFlagLimit() { private async validateFeatureFlagLimit() {
if (this.flagResolver.isEnabled('resourceLimits')) { const currentFlagCount = await this.featureToggleStore.count();
const currentFlagCount = await this.featureToggleStore.count(); const limit = this.resourceLimits.featureFlags;
const limit = this.resourceLimits.featureFlags; if (currentFlagCount >= limit) {
if (currentFlagCount >= limit) { throwExceedsLimitError(this.eventBus, {
throwExceedsLimitError(this.eventBus, { resource: 'feature flag',
resource: 'feature flag', limit,
limit, });
});
}
} }
} }

View File

@ -14,9 +14,7 @@ test('Should not allow to exceed project limit on create', async () => {
const projectService = createFakeProjectService({ const projectService = createFakeProjectService({
...createTestConfig(), ...createTestConfig(),
flagResolver: alwaysOnFlagResolver, flagResolver: alwaysOnFlagResolver,
resourceLimits: { resourceLimits: { projects: LIMIT },
projects: LIMIT,
},
eventBus: { eventBus: {
emit: () => {}, emit: () => {},
}, },
@ -37,9 +35,7 @@ test('Should not allow to exceed project limit on revive', async () => {
const projectService = createFakeProjectService({ const projectService = createFakeProjectService({
...createTestConfig(), ...createTestConfig(),
flagResolver: alwaysOnFlagResolver, flagResolver: alwaysOnFlagResolver,
resourceLimits: { resourceLimits: { projects: LIMIT },
projects: LIMIT,
},
eventBus: { eventBus: {
emit: () => {}, emit: () => {},
}, },

View File

@ -325,8 +325,6 @@ export default class ProjectService {
} }
async validateProjectLimit() { async validateProjectLimit() {
if (!this.flagResolver.isEnabled('resourceLimits')) return;
const limit = Math.max(this.resourceLimits.projects, 1); const limit = Math.max(this.resourceLimits.projects, 1);
const projectCount = await this.projectStore.count(); const projectCount = await this.projectStore.count();

View File

@ -13,9 +13,7 @@ test('Should not allow to exceed segment limit', async () => {
const segmentService = createFakeSegmentService({ const segmentService = createFakeSegmentService({
getLogger, getLogger,
flagResolver: alwaysOnFlagResolver, flagResolver: alwaysOnFlagResolver,
resourceLimits: { resourceLimits: { segments: LIMIT },
segments: LIMIT,
},
eventBus: { eventBus: {
emit: () => {}, emit: () => {},
}, },

View File

@ -129,8 +129,6 @@ export class SegmentService implements ISegmentService {
} }
async validateSegmentLimit() { async validateSegmentLimit() {
if (!this.flagResolver.isEnabled('resourceLimits')) return;
const limit = this.resourceLimits.segments; const limit = this.resourceLimits.segments;
const segmentCount = await this.segmentStore.count(); const segmentCount = await this.segmentStore.count();

View File

@ -7,9 +7,7 @@ import { createFakeApiTokenService } from '../features/api-tokens/createApiToken
const createServiceWithLimit = (limit: number) => { const createServiceWithLimit = (limit: number) => {
const config: IUnleashConfig = createTestConfig({ const config: IUnleashConfig = createTestConfig({
experimental: { experimental: {
flags: { flags: {},
resourceLimits: true,
},
}, },
}); });
config.resourceLimits.apiTokens = limit; config.resourceLimits.apiTokens = limit;

View File

@ -308,15 +308,13 @@ export class ApiTokenService {
} }
private async validateApiTokenLimit() { private async validateApiTokenLimit() {
if (this.flagResolver.isEnabled('resourceLimits')) { const currentTokenCount = await this.store.count();
const currentTokenCount = await this.store.count(); const limit = this.resourceLimits.apiTokens;
const limit = this.resourceLimits.apiTokens; if (currentTokenCount >= limit) {
if (currentTokenCount >= limit) { throwExceedsLimitError(this.eventBus, {
throwExceedsLimitError(this.eventBus, { resource: 'api token',
resource: 'api token', limit,
limit, });
});
}
} }
} }

View File

@ -56,7 +56,6 @@ export type IFlagKey =
| 'enableLegacyVariants' | 'enableLegacyVariants'
| 'navigationSidebar' | 'navigationSidebar'
| 'anonymizeProjectOwners' | 'anonymizeProjectOwners'
| 'resourceLimits'
| 'extendedMetrics' | 'extendedMetrics'
| 'removeUnsafeInlineStyleSrc' | 'removeUnsafeInlineStyleSrc'
| 'originMiddleware' | 'originMiddleware'
@ -278,10 +277,6 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_ANONYMIZE_PROJECT_OWNERS, process.env.UNLEASH_EXPERIMENTAL_ANONYMIZE_PROJECT_OWNERS,
false, false,
), ),
resourceLimits: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_RESOURCE_LIMITS,
false,
),
extendedMetrics: parseEnvVarBoolean( extendedMetrics: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_EXTENDED_METRICS, process.env.UNLEASH_EXPERIMENTAL_EXTENDED_METRICS,
false, false,

View File

@ -50,7 +50,6 @@ process.nextTick(async () => {
projectOverviewRefactorFeedback: true, projectOverviewRefactorFeedback: true,
manyStrategiesPagination: true, manyStrategiesPagination: true,
enableLegacyVariants: false, enableLegacyVariants: false,
resourceLimits: true,
extendedMetrics: true, extendedMetrics: true,
originMiddleware: true, originMiddleware: true,
newEventSearch: true, newEventSearch: true,