From e70be0739ba67e6fa95d3e8ce93d3fd7ba2433dd Mon Sep 17 00:00:00 2001 From: andreas-unleash Date: Tue, 25 Apr 2023 12:57:16 +0300 Subject: [PATCH] fix: BE protection for empty stickiness (#3615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'default' when creating or throw error when updating a flexibleRollout strategy with empty stickiness ## About the changes Closes # ### Important files ## Discussion points --------- Signed-off-by: andreas-unleash --- src/lib/services/feature-toggle-service.ts | 18 +++++ src/test/e2e/api/admin/feature.e2e.test.ts | 81 +++++++++++++++++++++- 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/src/lib/services/feature-toggle-service.ts b/src/lib/services/feature-toggle-service.ts index 7a5d0415524..f0648b100c0 100644 --- a/src/lib/services/feature-toggle-service.ts +++ b/src/lib/services/feature-toggle-service.ts @@ -224,6 +224,16 @@ class FeatureToggleService { 'You can not change the featureName for an activation strategy.', ); } + + if ( + strategy.parameters && + 'stickiness' in strategy.parameters && + strategy.parameters.stickiness === '' + ) { + throw new InvalidOperationError( + 'You can not have an empty string for stickiness.', + ); + } } async validateProjectCanAccessSegments( @@ -410,6 +420,14 @@ class FeatureToggleService { ); } + if ( + strategyConfig.parameters && + 'stickiness' in strategyConfig.parameters && + strategyConfig.parameters.stickiness === '' + ) { + strategyConfig.parameters.stickiness = 'default'; + } + try { const newFeatureStrategy = await this.featureStrategiesStore.createStrategyFeatureEnv({ diff --git a/src/test/e2e/api/admin/feature.e2e.test.ts b/src/test/e2e/api/admin/feature.e2e.test.ts index 4583c21c914..430c526ed39 100644 --- a/src/test/e2e/api/admin/feature.e2e.test.ts +++ b/src/test/e2e/api/admin/feature.e2e.test.ts @@ -17,8 +17,11 @@ let app: IUnleashTest; let db: ITestDb; const defaultStrategy = { - name: 'default', - parameters: {}, + name: 'flexibleRollout', + parameters: { + rollout: '100', + stickiness: '', + }, constraints: [], }; @@ -844,3 +847,77 @@ test('Can add and remove tags at the same time', async () => { expect(res.body.tags).toHaveLength(1); }); }); + +test('Should return "default" for stickiness when creating a flexibleRollout strategy with "" for stickiness', async () => { + const username = 'toggle-feature'; + const feature = { + name: 'test-featureA', + description: 'the #1 feature', + }; + const projectId = 'default'; + + await app.services.featureToggleServiceV2.createFeatureToggle( + projectId, + feature, + username, + ); + await app.services.featureToggleServiceV2.createStrategy( + defaultStrategy, + { projectId, featureName: feature.name, environment: DEFAULT_ENV }, + username, + ); + + await app.request + .get( + `/api/admin/projects/${projectId}/features/${feature.name}/environments/${DEFAULT_ENV}`, + ) + .expect((res) => { + const toggle = res.body; + expect(toggle.strategies).toHaveLength(1); + expect(toggle.strategies[0].parameters.stickiness).toBe('default'); + }); + + await app.request + .get(`/api/admin/features/${feature.name}`) + .expect((res) => { + const toggle = res.body; + expect(toggle.strategies).toHaveLength(1); + expect(toggle.strategies[0].parameters.stickiness).toBe('default'); + }); +}); + +test('Should throw error when updating a flexibleRollout strategy with "" for stickiness', async () => { + const username = 'toggle-feature'; + const feature = { + name: 'test-featureB', + description: 'the #1 feature', + }; + const projectId = 'default'; + + await app.services.featureToggleServiceV2.createFeatureToggle( + projectId, + feature, + username, + ); + await app.services.featureToggleServiceV2.createStrategy( + defaultStrategy, + { projectId, featureName: feature.name, environment: DEFAULT_ENV }, + username, + ); + + const featureToggle = + await app.services.featureToggleServiceV2.getFeatureToggle( + feature.name, + ); + + await app.request + .patch( + `/api/admin/projects/${projectId}/features/${feature.name}/environments/${DEFAULT_ENV}/strategies/${featureToggle.environments[0].strategies[0].id}`, + ) + .send(defaultStrategy) + .expect((res) => { + const result = res.body; + expect(res.status).toBe(400); + expect(result.error).toBe('Request validation failed'); + }); +});