diff --git a/src/lib/features/dependent-features/dependent-features-controller.ts b/src/lib/features/dependent-features/dependent-features-controller.ts index e7dc8e023f..3d8dffa2d6 100644 --- a/src/lib/features/dependent-features/dependent-features-controller.ts +++ b/src/lib/features/dependent-features/dependent-features-controller.ts @@ -117,6 +117,26 @@ export default class DependentFeaturesController extends Controller { }), ], }); + + this.route({ + method: 'delete', + path: PATH_DEPENDENCIES, + handler: this.deleteFeatureDependencies, + permission: UPDATE_FEATURE, + acceptAnyContentType: true, + middleware: [ + openApiService.validPath({ + tags: ['Features'], + summary: 'Deletes feature dependencies.', + description: 'Remove dependencies to all parent features.', + operationId: 'deleteFeatureDependencies', + responses: { + 200: emptyResponse, + ...getStandardResponses(401, 403, 404), + }, + }), + ], + }); } async addFeatureDependency( @@ -162,4 +182,22 @@ export default class DependentFeaturesController extends Controller { ); } } + + async deleteFeatureDependencies( + req: IAuthRequest, + res: Response, + ): Promise { + const { child } = req.params; + + if (this.config.flagResolver.isEnabled('dependentFeatures')) { + await this.dependentFeaturesService.deleteFeatureDependencies( + child, + ); + res.status(200).end(); + } else { + throw new InvalidOperationError( + 'Dependent features are not enabled', + ); + } + } } diff --git a/src/lib/features/dependent-features/dependent-features-service.ts b/src/lib/features/dependent-features/dependent-features-service.ts index 949659e28f..dacfc8f499 100644 --- a/src/lib/features/dependent-features/dependent-features-service.ts +++ b/src/lib/features/dependent-features/dependent-features-service.ts @@ -44,4 +44,8 @@ export class DependentFeaturesService { ): Promise { await this.dependentFeaturesStore.delete(dependency); } + + async deleteFeatureDependencies(feature: string): Promise { + await this.dependentFeaturesStore.deleteAll(feature); + } } diff --git a/src/lib/features/dependent-features/dependent-features-store-type.ts b/src/lib/features/dependent-features/dependent-features-store-type.ts index ed13e5cd71..2eefbf0e6b 100644 --- a/src/lib/features/dependent-features/dependent-features-store-type.ts +++ b/src/lib/features/dependent-features/dependent-features-store-type.ts @@ -4,4 +4,5 @@ export interface IDependentFeaturesStore { upsert(featureDependency: FeatureDependency): Promise; getChildren(parent: string): Promise; delete(dependency: FeatureDependencyId): Promise; + deleteAll(child: string): Promise; } diff --git a/src/lib/features/dependent-features/dependent-features-store.ts b/src/lib/features/dependent-features/dependent-features-store.ts index b8ee108aee..ff66744d2e 100644 --- a/src/lib/features/dependent-features/dependent-features-store.ts +++ b/src/lib/features/dependent-features/dependent-features-store.ts @@ -44,4 +44,8 @@ export class DependentFeaturesStore implements IDependentFeaturesStore { .andWhere('child', dependency.child) .del(); } + + async deleteAll(feature: string): Promise { + await this.db('dependent_features').andWhere('child', feature).del(); + } } diff --git a/src/lib/features/dependent-features/dependent.features.e2e.test.ts b/src/lib/features/dependent-features/dependent.features.e2e.test.ts index e45cd7aa23..a375800e6d 100644 --- a/src/lib/features/dependent-features/dependent.features.e2e.test.ts +++ b/src/lib/features/dependent-features/dependent.features.e2e.test.ts @@ -56,7 +56,18 @@ const deleteFeatureDependency = async ( .expect(expectedCode); }; -test('should add feature dependency', async () => { +const deleteFeatureDependencies = async ( + childFeature: string, + expectedCode = 200, +) => { + return app.request + .delete( + `/api/admin/projects/default/features/${childFeature}/dependencies`, + ) + .expect(expectedCode); +}; + +test('should add and delete feature dependencies', async () => { const parent = uuidv4(); const child = uuidv4(); await app.createFeature(parent); @@ -73,7 +84,8 @@ test('should add feature dependency', async () => { variants: ['variantB'], }); - await deleteFeatureDependency(child, parent); + await deleteFeatureDependency(child, parent); // single + await deleteFeatureDependencies(child); // all }); test('should not allow to add a parent dependency to a feature that already has children', async () => { diff --git a/src/lib/features/dependent-features/fake-dependent-features-store.ts b/src/lib/features/dependent-features/fake-dependent-features-store.ts index 46fbaac744..185ac1ebf6 100644 --- a/src/lib/features/dependent-features/fake-dependent-features-store.ts +++ b/src/lib/features/dependent-features/fake-dependent-features-store.ts @@ -12,4 +12,8 @@ export class FakeDependentFeaturesStore implements IDependentFeaturesStore { delete(): Promise { return Promise.resolve(); } + + deleteAll(): Promise { + return Promise.resolve(); + } }