mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-24 01:18:01 +02: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,
|
"actionSetFilterValues": 25,
|
||||||
"actionSetFilters": 5,
|
"actionSetFilters": 5,
|
||||||
"actionSetsPerProject": 5,
|
"actionSetsPerProject": 5,
|
||||||
|
"constraintValues": 250,
|
||||||
"environments": 50,
|
"environments": 50,
|
||||||
"featureEnvironmentStrategies": 30,
|
"featureEnvironmentStrategies": 30,
|
||||||
"segmentValues": 1000,
|
"segmentValues": 1000,
|
||||||
|
@ -653,6 +653,10 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
|
|||||||
process.env.UNLEASH_FEATURE_ENVIRONMENT_STRATEGIES_LIMIT,
|
process.env.UNLEASH_FEATURE_ENVIRONMENT_STRATEGIES_LIMIT,
|
||||||
30,
|
30,
|
||||||
),
|
),
|
||||||
|
constraintValues: parseEnvVarNumber(
|
||||||
|
process.env.UNLEASH_CONSTRAINT_VALUES_LIMIT,
|
||||||
|
250,
|
||||||
|
),
|
||||||
environments: parseEnvVarNumber(
|
environments: parseEnvVarNumber(
|
||||||
process.env.UNLEASH_ENVIRONMENTS_LIMIT,
|
process.env.UNLEASH_ENVIRONMENTS_LIMIT,
|
||||||
50,
|
50,
|
||||||
|
@ -366,15 +366,14 @@ class FeatureToggleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateStrategyLimit(
|
async validateStrategyLimit(featureEnv: {
|
||||||
featureEnv: {
|
projectId: string;
|
||||||
projectId: string;
|
environment: string;
|
||||||
environment: string;
|
featureName: string;
|
||||||
featureName: string;
|
}) {
|
||||||
},
|
|
||||||
limit: number,
|
|
||||||
) {
|
|
||||||
if (!this.flagResolver.isEnabled('resourceLimits')) return;
|
if (!this.flagResolver.isEnabled('resourceLimits')) return;
|
||||||
|
|
||||||
|
const limit = this.resourceLimits.featureEnvironmentStrategies;
|
||||||
const existingCount = (
|
const existingCount = (
|
||||||
await this.featureStrategiesStore.getStrategiesForFeatureEnv(
|
await this.featureStrategiesStore.getStrategiesForFeatureEnv(
|
||||||
featureEnv.projectId,
|
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(
|
async validateStrategyType(
|
||||||
strategyName: string | undefined,
|
strategyName: string | undefined,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -632,6 +647,7 @@ class FeatureToggleService {
|
|||||||
strategyConfig.constraints &&
|
strategyConfig.constraints &&
|
||||||
strategyConfig.constraints.length > 0
|
strategyConfig.constraints.length > 0
|
||||||
) {
|
) {
|
||||||
|
this.validateContraintValuesLimit(strategyConfig.constraints);
|
||||||
strategyConfig.constraints = await this.validateConstraints(
|
strategyConfig.constraints = await this.validateConstraints(
|
||||||
strategyConfig.constraints,
|
strategyConfig.constraints,
|
||||||
);
|
);
|
||||||
@ -653,10 +669,11 @@ class FeatureToggleService {
|
|||||||
strategyConfig.variants = fixedVariants;
|
strategyConfig.variants = fixedVariants;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.validateStrategyLimit(
|
await this.validateStrategyLimit({
|
||||||
{ featureName, projectId, environment },
|
featureName,
|
||||||
this.resourceLimits.featureEnvironmentStrategies,
|
projectId,
|
||||||
);
|
environment,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newFeatureStrategy =
|
const newFeatureStrategy =
|
||||||
@ -789,6 +806,7 @@ class FeatureToggleService {
|
|||||||
|
|
||||||
if (existingStrategy.id === id) {
|
if (existingStrategy.id === id) {
|
||||||
if (updates.constraints && updates.constraints.length > 0) {
|
if (updates.constraints && updates.constraints.length > 0) {
|
||||||
|
this.validateContraintValuesLimit(updates.constraints);
|
||||||
updates.constraints = await this.validateConstraints(
|
updates.constraints = await this.validateConstraints(
|
||||||
updates.constraints,
|
updates.constraints,
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createFakeFeatureToggleService } from '../createFeatureToggleService';
|
import { createFakeFeatureToggleService } from '../createFeatureToggleService';
|
||||||
import type {
|
import type {
|
||||||
IAuditUser,
|
IAuditUser,
|
||||||
|
IConstraint,
|
||||||
IFlagResolver,
|
IFlagResolver,
|
||||||
IStrategyConfig,
|
IStrategyConfig,
|
||||||
IUnleashConfig,
|
IUnleashConfig,
|
||||||
@ -43,3 +44,41 @@ test('Should not allow to exceed strategy limit', async () => {
|
|||||||
`Strategy limit of ${LIMIT} exceeded`,
|
`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',
|
'signalEndpoints',
|
||||||
'signalTokensPerEndpoint',
|
'signalTokensPerEndpoint',
|
||||||
'featureEnvironmentStrategies',
|
'featureEnvironmentStrategies',
|
||||||
|
'constraintValues',
|
||||||
'environments',
|
'environments',
|
||||||
],
|
],
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
@ -69,6 +70,12 @@ export const resourceLimitsSchema = {
|
|||||||
description:
|
description:
|
||||||
'The maximum number of feature environment strategies allowed.',
|
'The maximum number of feature environment strategies allowed.',
|
||||||
},
|
},
|
||||||
|
constraintValues: {
|
||||||
|
type: 'integer',
|
||||||
|
example: 250,
|
||||||
|
description:
|
||||||
|
'The maximum number of values for a single constraint.',
|
||||||
|
},
|
||||||
environments: {
|
environments: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
minimum: 1,
|
minimum: 1,
|
||||||
|
Loading…
Reference in New Issue
Block a user