From 4a2d1b03643ec2a8c73492584260d51ec95b3978 Mon Sep 17 00:00:00 2001 From: andreas-unleash Date: Fri, 26 Jan 2024 16:02:17 +0200 Subject: [PATCH] fix: return 400 when enabling env of archived toggle (#6049) Creates a new ArchivedFeatureError. Throw this error when trying to toggle a feature environment for an archived feature. Closes https://github.com/orgs/Unleash/projects/8/views/1?pane=issue&itemId=51242922 Signed-off-by: andreas-unleash --- src/lib/error/archivedfeature-error.ts | 13 ++++++ .../feature-toggle/feature-toggle-service.ts | 14 ++++++ .../tests/feature-toggles.e2e.test.ts | 44 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/lib/error/archivedfeature-error.ts diff --git a/src/lib/error/archivedfeature-error.ts b/src/lib/error/archivedfeature-error.ts new file mode 100644 index 000000000000..6b5298839207 --- /dev/null +++ b/src/lib/error/archivedfeature-error.ts @@ -0,0 +1,13 @@ +import { UnleashError } from './unleash-error'; + +class ArchivedFeatureError extends UnleashError { + statusCode = 400; + + constructor( + message: string = 'Cannot perform this operation on archived features', + ) { + super(message); + } +} +export default ArchivedFeatureError; +module.exports = ArchivedFeatureError; diff --git a/src/lib/features/feature-toggle/feature-toggle-service.ts b/src/lib/features/feature-toggle/feature-toggle-service.ts index 496a1b6170cb..82e66043a3c3 100644 --- a/src/lib/features/feature-toggle/feature-toggle-service.ts +++ b/src/lib/features/feature-toggle/feature-toggle-service.ts @@ -104,6 +104,7 @@ import { IDependentFeaturesReadModel } from '../dependent-features/dependent-fea import EventService from '../events/event-service'; import { DependentFeaturesService } from '../dependent-features/dependent-features-service'; import { FeatureToggleInsert } from './feature-toggle-store'; +import ArchivedFeatureError from '../../error/archivedfeature-error'; interface IFeatureContext { featureName: string; @@ -259,6 +260,17 @@ class FeatureToggleService { } } + async validateFeatureIsNotArchived( + featureName: string, + project: string, + ): Promise { + const toggle = await this.featureToggleStore.get(featureName); + + if (toggle.archived || Boolean(toggle.archivedAt)) { + throw new ArchivedFeatureError(); + } + } + async validateNoChildren(featureName: string): Promise { const children = await this.dependentFeaturesReadModel.getChildren([ featureName, @@ -1748,6 +1760,8 @@ class FeatureToggleService { ); } + await this.validateFeatureIsNotArchived(featureName, project); + if (enabled) { const strategies = await this.getStrategiesForEnvironment( project, diff --git a/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts b/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts index 2d30c5e89dde..69a6c6fc7399 100644 --- a/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts +++ b/src/lib/features/feature-toggle/tests/feature-toggles.e2e.test.ts @@ -1707,6 +1707,50 @@ test('Disabling environment creates a FEATURE_ENVIRONMENT_DISABLED event', async expect(ourFeatureEvent).toBeTruthy(); }); +test('Returns 400 when toggling environment of archived feature', async () => { + const environment = 'environment_test_archived'; + const featureName = 'test_archived_feature'; + + // Create environment + await db.stores.environmentStore.create({ + name: environment, + type: 'test', + }); + // Connect environment to project + await app.request + .post('/api/admin/projects/default/environments') + .send({ environment }) + .expect(200); + + // Create feature + await app.request + .post('/api/admin/projects/default/features') + .send({ + name: featureName, + }) + .set('Content-Type', 'application/json') + .expect(201); + // Archive feature + await app.request + .delete(`/api/admin/projects/default/features/${featureName}`) + .set('Content-Type', 'application/json') + .expect(202); + + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/strategies`, + ) + .send({ name: 'default', constraints: [] }) + .expect(200); + + await app.request + .post( + `/api/admin/projects/default/features/${featureName}/environments/${environment}/on`, + ) + .set('Content-Type', 'application/json') + .expect(400); +}); + test('Can delete strategy from feature toggle', async () => { const envName = 'del-strategy'; const featureName = 'feature.strategy.toggle.delete.strategy';