diff --git a/src/lib/db/access-store.ts b/src/lib/db/access-store.ts index 80f2c371f9..5c9c35f659 100644 --- a/src/lib/db/access-store.ts +++ b/src/lib/db/access-store.ts @@ -27,6 +27,7 @@ const T = { ROLE_PERMISSION: 'role_permission', PERMISSIONS: 'permissions', PERMISSION_TYPES: 'permission_types', + CHANGE_REQUEST_SETTINGS: 'change_request_settings', }; interface IPermissionRow { @@ -448,4 +449,17 @@ export class AccessStore implements IAccessStore { [destinationEnvironment, sourceEnvironment], ); } + + async isChangeRequestsEnabled( + project: string, + environment: string, + ): Promise { + const result = await this.db.raw( + `SELECT EXISTS(SELECT 1 FROM ${T.CHANGE_REQUEST_SETTINGS} + WHERE environment = ? and project = ?) AS present`, + [environment, project], + ); + const { present } = result.rows[0]; + return present; + } } diff --git a/src/lib/services/access-service.ts b/src/lib/services/access-service.ts index 4896f95103..594f8505d1 100644 --- a/src/lib/services/access-service.ts +++ b/src/lib/services/access-service.ts @@ -542,4 +542,11 @@ export class AccessService { await this.validateRoleIsUnique(role.name, existingId); return cleanedRole; } + + async isChangeRequestsEnabled( + project: string, + environment: string, + ): Promise { + return this.store.isChangeRequestsEnabled(project, environment); + } } diff --git a/src/lib/services/feature-toggle-service.ts b/src/lib/services/feature-toggle-service.ts index 34fa48b9f0..a8eb57a7b8 100644 --- a/src/lib/services/feature-toggle-service.ts +++ b/src/lib/services/feature-toggle-service.ts @@ -314,6 +314,11 @@ class FeatureToggleService { const { featureName, projectId, environment } = context; await this.validateFeatureContext(context); + if (await this.changeRequestsEnabled(projectId, environment)) { + throw new Error( + `Strategies can only be created through change requests for ${environment} environment`, + ); + } if (strategyConfig.constraints?.length > 0) { strategyConfig.constraints = await this.validateConstraints( strategyConfig.constraints, @@ -382,6 +387,12 @@ class FeatureToggleService { const existingStrategy = await this.featureStrategiesStore.get(id); this.validateFeatureStrategyContext(existingStrategy, context); + if (await this.changeRequestsEnabled(projectId, environment)) { + throw new Error( + `Strategies can only be updated through change requests for ${environment} environment`, + ); + } + if (existingStrategy.id === id) { if (updates.constraints?.length > 0) { updates.constraints = await this.validateConstraints( @@ -430,6 +441,12 @@ class FeatureToggleService { ): Promise> { const { projectId, environment, featureName } = context; + if (await this.changeRequestsEnabled(projectId, environment)) { + throw new Error( + `Strategies can only be updated through change requests for ${environment} environment`, + ); + } + const existingStrategy = await this.featureStrategiesStore.get(id); this.validateFeatureStrategyContext(existingStrategy, context); @@ -482,6 +499,12 @@ class FeatureToggleService { const { featureName, projectId, environment } = context; this.validateFeatureStrategyContext(existingStrategy, context); + if (await this.changeRequestsEnabled(projectId, environment)) { + throw new Error( + `Strategies can only deleted updated through change requests for ${environment} environment`, + ); + } + await this.featureStrategiesStore.delete(id); const tags = await this.tagStore.getAllTagsForFeature(featureName); @@ -903,6 +926,12 @@ class FeatureToggleService { createdBy: string, user?: User, ): Promise { + if (await this.changeRequestsEnabled(project, environment)) { + throw new Error( + `Features can only be updated through change requests for ${environment} environment`, + ); + } + const hasEnvironment = await this.featureEnvironmentStore.featureHasEnvironment( environment, @@ -928,7 +957,11 @@ class FeatureToggleService { if (canAddStrategies) { await this.createStrategy( getDefaultStrategy(featureName), - { environment, projectId: project, featureName }, + { + environment, + projectId: project, + featureName, + }, createdBy, ); } else { @@ -961,6 +994,7 @@ class FeatureToggleService { } return feature; } + throw new NotFoundError( `Could not find environment ${environment} for feature: ${featureName}`, ); @@ -1186,6 +1220,13 @@ class FeatureToggleService { }); return variableVariants.concat(fixedVariants); } + + changeRequestsEnabled( + project: string, + environment: string, + ): Promise { + return this.accessService.isChangeRequestsEnabled(project, environment); + } } export default FeatureToggleService; diff --git a/src/lib/types/stores/access-store.ts b/src/lib/types/stores/access-store.ts index 96662b2101..a4a50596cc 100644 --- a/src/lib/types/stores/access-store.ts +++ b/src/lib/types/stores/access-store.ts @@ -132,4 +132,9 @@ export interface IAccessStore extends Store { sourceEnvironment: string, destinationEnvironment: string, ): Promise; + + isChangeRequestsEnabled( + project: string, + environment: string, + ): Promise; } diff --git a/src/test/fixtures/fake-access-store.ts b/src/test/fixtures/fake-access-store.ts index 280856286e..0fbd45848a 100644 --- a/src/test/fixtures/fake-access-store.ts +++ b/src/test/fixtures/fake-access-store.ts @@ -11,6 +11,13 @@ import { import { IPermission } from 'lib/types/model'; class AccessStoreMock implements IAccessStore { + isChangeRequestsEnabled( + project: string, + environment: string, + ): Promise { + throw new Error('Method not implemented.'); + } + addAccessToProject( users: IAccessInfo[], groups: IAccessInfo[],