From 3fe110f1557c5f65289def5623c9162ed7e49d4e Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 10 Jul 2024 09:11:22 +0200 Subject: [PATCH] feat: exclude archived features in max reporting (#7559) --- .../feature-strategies-read-model.ts | 24 ++++-- ...eature-toggle-strategies-store.e2e.test.ts | 84 +++++++++++-------- 2 files changed, 66 insertions(+), 42 deletions(-) diff --git a/src/lib/features/feature-toggle/feature-strategies-read-model.ts b/src/lib/features/feature-toggle/feature-strategies-read-model.ts index 90fcfb8f5a..322033c3fe 100644 --- a/src/lib/features/feature-toggle/feature-strategies-read-model.ts +++ b/src/lib/features/feature-toggle/feature-strategies-read-model.ts @@ -7,12 +7,23 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel { constructor(db: Db) { this.db = db; } + + private activeStrategies() { + return this.db('feature_strategies') + .leftJoin( + 'features', + 'features.name', + 'feature_strategies.feature_name', + ) + .where('features.archived_at', null); + } + async getMaxFeatureEnvironmentStrategies(): Promise<{ feature: string; environment: string; count: number; } | null> { - const rows = await this.db('feature_strategies') + const rows = await this.activeStrategies() .select('feature_name', 'environment') .count('id as strategy_count') .groupBy('feature_name', 'environment') @@ -32,7 +43,7 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel { feature: string; count: number; } | null> { - const rows = await this.db('feature_strategies') + const rows = await this.activeStrategies() .select('feature_name') .count('id as strategy_count') .groupBy('feature_name') @@ -52,7 +63,7 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel { environment: string; count: number; } | null> { - const rows = await this.db('feature_strategies') + const rows = await this.activeStrategies() .select( 'feature_name', 'environment', @@ -60,9 +71,9 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel { "MAX(coalesce(jsonb_array_length(constraint_value->'values'), 0)) as max_values_count", ), ) - .from( + .crossJoin( this.db.raw( - 'feature_strategies, jsonb_array_elements(constraints) AS constraint_value', + `jsonb_array_elements(constraints) AS constraint_value`, ), ) .groupBy('feature_name', 'environment') @@ -77,12 +88,13 @@ export class FeatureStrategiesReadModel implements IFeatureStrategiesReadModel { } : null; } + async getMaxConstraintsPerStrategy(): Promise<{ feature: string; environment: string; count: number; } | null> { - const rows = await this.db('feature_strategies') + const rows = await this.activeStrategies() .select( 'feature_name', 'environment', diff --git a/src/lib/features/feature-toggle/tests/feature-toggle-strategies-store.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggle-strategies-store.e2e.test.ts index 6b88420712..fe3095d88b 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggle-strategies-store.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggle-strategies-store.e2e.test.ts @@ -5,6 +5,7 @@ import dbInit, { } from '../../../../test/e2e/helpers/database-init'; import getLogger from '../../../../test/fixtures/no-logger'; import type { + IConstraint, IFeatureStrategiesReadModel, IProjectStore, IUnleashStores, @@ -294,6 +295,32 @@ describe('max metrics collection', () => { }); }); + const bigConstraint = (maxValueCount: number) => { + return { + values: Array.from({ length: maxValueCount }, (_, i) => + i.toString(), + ), + operator: 'IN', + contextName: 'appName', + } as const; + }; + + const strategyWithConstraints = ( + feature: string, + constraint: IConstraint, + ) => { + return { + strategyName: 'gradualRollout', + projectId: 'default', + environment: 'default', + featureName: feature, + constraints: [constraint], + + sortOrder: 0, + parameters: {}, + }; + }; + test('Read feature with max number of constraint values', async () => { const flagA = await featureToggleStore.create('default', { name: randomId(), @@ -305,48 +332,33 @@ describe('max metrics collection', () => { createdByUserId: 9999, }); + const flagC = await featureToggleStore.create('default', { + name: randomId(), + createdByUserId: 9999, + }); + const maxConstraintValuesBefore = await featureStrategiesReadModel.getMaxConstraintValues(); expect(maxConstraintValuesBefore).toBe(null); const maxValueCount = 100; - await featureStrategiesStore.createStrategyFeatureEnv({ - strategyName: 'gradualRollout', - projectId: 'default', - environment: 'default', - featureName: flagA.name, - constraints: [ - { - values: ['only one'], - operator: 'IN', - contextName: 'appName', - }, - { - values: Array.from({ length: maxValueCount }, (_, i) => - i.toString(), - ), - operator: 'IN', - contextName: 'appName', - }, - ], + await featureStrategiesStore.createStrategyFeatureEnv( + strategyWithConstraints(flagA.name, bigConstraint(maxValueCount)), + ); + await featureStrategiesStore.createStrategyFeatureEnv( + strategyWithConstraints(flagB.name, { + operator: 'IN', + contextName: 'appName', + }), + ); + await featureStrategiesStore.createStrategyFeatureEnv( + strategyWithConstraints( + flagC.name, + bigConstraint(maxValueCount + 1), + ), + ); - sortOrder: 0, - parameters: {}, - }); - await featureStrategiesStore.createStrategyFeatureEnv({ - strategyName: 'gradualRollout', - projectId: 'default', - environment: 'default', - featureName: flagB.name, - constraints: [ - { - operator: 'IN', - contextName: 'appName', - }, - ], - sortOrder: 0, - parameters: {}, - }); + await featureToggleStore.archive(flagC.name); const maxConstraintValues = await featureStrategiesReadModel.getMaxConstraintValues();