mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-28 00:06:53 +01:00
feat: constraint values limit (#7498)
This commit is contained in:
parent
c907199d23
commit
2cfd71f34e
@ -196,6 +196,7 @@ exports[`should create default config 1`] = `
|
||||
"actionSetFilterValues": 25,
|
||||
"actionSetFilters": 5,
|
||||
"actionSetsPerProject": 5,
|
||||
"constraintValues": 250,
|
||||
"environments": 50,
|
||||
"featureEnvironmentStrategies": 30,
|
||||
"segmentValues": 1000,
|
||||
|
@ -653,6 +653,10 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
||||
process.env.UNLEASH_FEATURE_ENVIRONMENT_STRATEGIES_LIMIT,
|
||||
30,
|
||||
),
|
||||
constraintValues: parseEnvVarNumber(
|
||||
process.env.UNLEASH_CONSTRAINT_VALUES_LIMIT,
|
||||
250,
|
||||
),
|
||||
environments: parseEnvVarNumber(
|
||||
process.env.UNLEASH_ENVIRONMENTS_LIMIT,
|
||||
50,
|
||||
|
@ -366,15 +366,14 @@ class FeatureToggleService {
|
||||
}
|
||||
}
|
||||
|
||||
async validateStrategyLimit(
|
||||
featureEnv: {
|
||||
projectId: string;
|
||||
environment: string;
|
||||
featureName: string;
|
||||
},
|
||||
limit: number,
|
||||
) {
|
||||
async validateStrategyLimit(featureEnv: {
|
||||
projectId: string;
|
||||
environment: string;
|
||||
featureName: string;
|
||||
}) {
|
||||
if (!this.flagResolver.isEnabled('resourceLimits')) return;
|
||||
|
||||
const limit = this.resourceLimits.featureEnvironmentStrategies;
|
||||
const existingCount = (
|
||||
await this.featureStrategiesStore.getStrategiesForFeatureEnv(
|
||||
featureEnv.projectId,
|
||||
@ -387,6 +386,22 @@ class FeatureToggleService {
|
||||
}
|
||||
}
|
||||
|
||||
validateContraintValuesLimit(updatedConstrains: IConstraint[]) {
|
||||
if (!this.flagResolver.isEnabled('resourceLimits')) return;
|
||||
|
||||
const limit = this.resourceLimits.constraintValues;
|
||||
const constraintOverLimit = updatedConstrains.find(
|
||||
(constraint) =>
|
||||
Array.isArray(constraint.values) &&
|
||||
constraint.values?.length > limit,
|
||||
);
|
||||
if (constraintOverLimit) {
|
||||
throw new BadDataError(
|
||||
`Constraint values limit of ${limit} is exceeded for ${constraintOverLimit.contextName}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async validateStrategyType(
|
||||
strategyName: string | undefined,
|
||||
): Promise<void> {
|
||||
@ -632,6 +647,7 @@ class FeatureToggleService {
|
||||
strategyConfig.constraints &&
|
||||
strategyConfig.constraints.length > 0
|
||||
) {
|
||||
this.validateContraintValuesLimit(strategyConfig.constraints);
|
||||
strategyConfig.constraints = await this.validateConstraints(
|
||||
strategyConfig.constraints,
|
||||
);
|
||||
@ -653,10 +669,11 @@ class FeatureToggleService {
|
||||
strategyConfig.variants = fixedVariants;
|
||||
}
|
||||
|
||||
await this.validateStrategyLimit(
|
||||
{ featureName, projectId, environment },
|
||||
this.resourceLimits.featureEnvironmentStrategies,
|
||||
);
|
||||
await this.validateStrategyLimit({
|
||||
featureName,
|
||||
projectId,
|
||||
environment,
|
||||
});
|
||||
|
||||
try {
|
||||
const newFeatureStrategy =
|
||||
@ -789,6 +806,7 @@ class FeatureToggleService {
|
||||
|
||||
if (existingStrategy.id === id) {
|
||||
if (updates.constraints && updates.constraints.length > 0) {
|
||||
this.validateContraintValuesLimit(updates.constraints);
|
||||
updates.constraints = await this.validateConstraints(
|
||||
updates.constraints,
|
||||
);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { createFakeFeatureToggleService } from '../createFeatureToggleService';
|
||||
import type {
|
||||
IAuditUser,
|
||||
IConstraint,
|
||||
IFlagResolver,
|
||||
IStrategyConfig,
|
||||
IUnleashConfig,
|
||||
@ -43,3 +44,41 @@ test('Should not allow to exceed strategy limit', async () => {
|
||||
`Strategy limit of ${LIMIT} exceeded`,
|
||||
);
|
||||
});
|
||||
|
||||
test('Should not allow to exceed constraint values limit', async () => {
|
||||
const LIMIT = 3;
|
||||
const { featureToggleService, featureToggleStore } =
|
||||
createFakeFeatureToggleService({
|
||||
getLogger,
|
||||
flagResolver: alwaysOnFlagResolver,
|
||||
resourceLimits: {
|
||||
constraintValues: LIMIT,
|
||||
},
|
||||
} as unknown as IUnleashConfig);
|
||||
|
||||
const addStrategyWithConstraints = (constraints: IConstraint[]) =>
|
||||
featureToggleService.unprotectedCreateStrategy(
|
||||
{
|
||||
name: 'default',
|
||||
featureName: 'feature',
|
||||
constraints,
|
||||
} as IStrategyConfig,
|
||||
{ projectId: 'default', featureName: 'feature' } as any,
|
||||
{} as IAuditUser,
|
||||
);
|
||||
await featureToggleStore.create('default', {
|
||||
name: 'feature',
|
||||
createdByUserId: 1,
|
||||
});
|
||||
await expect(() =>
|
||||
addStrategyWithConstraints([
|
||||
{
|
||||
contextName: 'userId',
|
||||
operator: 'IN',
|
||||
values: ['1', '2', '3', '4'],
|
||||
},
|
||||
]),
|
||||
).rejects.toThrow(
|
||||
`Constraint values limit of ${LIMIT} is exceeded for userId`,
|
||||
);
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ export const resourceLimitsSchema = {
|
||||
'signalEndpoints',
|
||||
'signalTokensPerEndpoint',
|
||||
'featureEnvironmentStrategies',
|
||||
'constraintValues',
|
||||
'environments',
|
||||
],
|
||||
additionalProperties: false,
|
||||
@ -69,6 +70,12 @@ export const resourceLimitsSchema = {
|
||||
description:
|
||||
'The maximum number of feature environment strategies allowed.',
|
||||
},
|
||||
constraintValues: {
|
||||
type: 'integer',
|
||||
example: 250,
|
||||
description:
|
||||
'The maximum number of values for a single constraint.',
|
||||
},
|
||||
environments: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
|
Loading…
Reference in New Issue
Block a user