mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: disable projects (#1085)
This commit is contained in:
		
							parent
							
								
									ce16c5a77b
								
							
						
					
					
						commit
						ec60f4485c
					
				
							
								
								
									
										23
									
								
								src/lib/error/minimum-one-environment-error.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/lib/error/minimum-one-environment-error.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | class MinimumOneEnvironmentError extends Error { | ||||||
|  |     constructor(message: string) { | ||||||
|  |         super(); | ||||||
|  |         Error.captureStackTrace(this, this.constructor); | ||||||
|  | 
 | ||||||
|  |         this.name = this.constructor.name; | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     toJSON(): object { | ||||||
|  |         return { | ||||||
|  |             isJoi: true, | ||||||
|  |             name: this.constructor.name, | ||||||
|  |             details: [ | ||||||
|  |                 { | ||||||
|  |                     message: this.message, | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | export default MinimumOneEnvironmentError; | ||||||
|  | module.exports = MinimumOneEnvironmentError; | ||||||
| @ -54,6 +54,8 @@ export const handleErrors: ( | |||||||
|             return res.status(400).json(error).end(); |             return res.status(400).json(error).end(); | ||||||
|         case 'IncompatibleProjectError': |         case 'IncompatibleProjectError': | ||||||
|             return res.status(403).json(error).end(); |             return res.status(403).json(error).end(); | ||||||
|  |         case 'MinimumOneEnvironmentError': | ||||||
|  |             return res.status(400).json(error).end(); | ||||||
|         default: |         default: | ||||||
|             logger.error('Server failed executing request', error); |             logger.error('Server failed executing request', error); | ||||||
|             return res.status(500).end(); |             return res.status(500).end(); | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import NotFoundError from '../error/notfound-error'; | |||||||
| import { IEnvironmentStore } from '../types/stores/environment-store'; | import { IEnvironmentStore } from '../types/stores/environment-store'; | ||||||
| import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-store'; | import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-store'; | ||||||
| import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store'; | import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store'; | ||||||
|  | import { IProjectStore } from 'lib/types/stores/project-store'; | ||||||
|  | import MinimumOneEnvironmentError from '../error/minimum-one-environment-error'; | ||||||
| 
 | 
 | ||||||
| export default class EnvironmentService { | export default class EnvironmentService { | ||||||
|     private logger: Logger; |     private logger: Logger; | ||||||
| @ -17,6 +19,8 @@ export default class EnvironmentService { | |||||||
| 
 | 
 | ||||||
|     private featureStrategiesStore: IFeatureStrategiesStore; |     private featureStrategiesStore: IFeatureStrategiesStore; | ||||||
| 
 | 
 | ||||||
|  |     private projectStore: IProjectStore; | ||||||
|  | 
 | ||||||
|     private featureEnvironmentStore: IFeatureEnvironmentStore; |     private featureEnvironmentStore: IFeatureEnvironmentStore; | ||||||
| 
 | 
 | ||||||
|     constructor( |     constructor( | ||||||
| @ -24,11 +28,13 @@ export default class EnvironmentService { | |||||||
|             environmentStore, |             environmentStore, | ||||||
|             featureStrategiesStore, |             featureStrategiesStore, | ||||||
|             featureEnvironmentStore, |             featureEnvironmentStore, | ||||||
|  |             projectStore, | ||||||
|         }: Pick< |         }: Pick< | ||||||
|             IUnleashStores, |             IUnleashStores, | ||||||
|             | 'environmentStore' |             | 'environmentStore' | ||||||
|             | 'featureStrategiesStore' |             | 'featureStrategiesStore' | ||||||
|             | 'featureEnvironmentStore' |             | 'featureEnvironmentStore' | ||||||
|  |             | 'projectStore' | ||||||
|         >, |         >, | ||||||
|         { getLogger }: Pick<IUnleashConfig, 'getLogger'>, |         { getLogger }: Pick<IUnleashConfig, 'getLogger'>, | ||||||
|     ) { |     ) { | ||||||
| @ -36,6 +42,7 @@ export default class EnvironmentService { | |||||||
|         this.environmentStore = environmentStore; |         this.environmentStore = environmentStore; | ||||||
|         this.featureStrategiesStore = featureStrategiesStore; |         this.featureStrategiesStore = featureStrategiesStore; | ||||||
|         this.featureEnvironmentStore = featureEnvironmentStore; |         this.featureEnvironmentStore = featureEnvironmentStore; | ||||||
|  |         this.projectStore = projectStore; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async getAll(): Promise<IEnvironment[]> { |     async getAll(): Promise<IEnvironment[]> { | ||||||
| @ -91,13 +98,23 @@ export default class EnvironmentService { | |||||||
|         environment: string, |         environment: string, | ||||||
|         projectId: string, |         projectId: string, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         await this.featureEnvironmentStore.disconnectFeatures( |         const projectEnvs = await this.projectStore.getEnvironmentsForProject( | ||||||
|             environment, |  | ||||||
|             projectId, |             projectId, | ||||||
|         ); |         ); | ||||||
|         await this.featureEnvironmentStore.disconnectProject( | 
 | ||||||
|             environment, |         if (projectEnvs.length > 1) { | ||||||
|             projectId, |             await this.featureEnvironmentStore.disconnectFeatures( | ||||||
|  |                 environment, | ||||||
|  |                 projectId, | ||||||
|  |             ); | ||||||
|  |             await this.featureEnvironmentStore.disconnectProject( | ||||||
|  |                 environment, | ||||||
|  |                 projectId, | ||||||
|  |             ); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         throw new MinimumOneEnvironmentError( | ||||||
|  |             'You must always have one active environment', | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -85,3 +85,20 @@ test('Should remove environment from project', async () => { | |||||||
| 
 | 
 | ||||||
|     expect(envs).toHaveLength(1); |     expect(envs).toHaveLength(1); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('Should not remove environment from project if project only has one environment enabled', async () => { | ||||||
|  |     await app.request | ||||||
|  |         .delete(`/api/admin/projects/default/environments/default`) | ||||||
|  |         .expect(400) | ||||||
|  |         .expect((r) => { | ||||||
|  |             expect(r.body.details[0].message).toBe( | ||||||
|  |                 'You must always have one active environment', | ||||||
|  |             ); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     const envs = await db.stores.projectStore.getEnvironmentsForProject( | ||||||
|  |         'default', | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     expect(envs).toHaveLength(1); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -53,7 +53,12 @@ test('returns feature toggle for default env', async () => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('returns feature toggle for default env even if it is removed from project', async () => { | test('returns feature toggle for default env even if it is removed from project', async () => { | ||||||
|     await app.services.environmentService.removeEnvironmentFromProject( |     await db.stores.featureEnvironmentStore.disconnectFeatures( | ||||||
|  |         'default', | ||||||
|  |         'default', | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     await db.stores.featureEnvironmentStore.disconnectProject( | ||||||
|         'default', |         'default', | ||||||
|         'default', |         'default', | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -109,10 +109,11 @@ test('Adding same environment twice should throw a NameExistsError', async () => | |||||||
|         name: 'uniqueness-test', |         name: 'uniqueness-test', | ||||||
|         type: 'production', |         type: 'production', | ||||||
|     }); |     }); | ||||||
|  |     await service.addEnvironmentToProject('uniqueness-test', 'default'); | ||||||
|  | 
 | ||||||
|     await service.removeEnvironmentFromProject('test-connection', 'default'); |     await service.removeEnvironmentFromProject('test-connection', 'default'); | ||||||
|     await service.removeEnvironmentFromProject('removal-test', 'default'); |     await service.removeEnvironmentFromProject('removal-test', 'default'); | ||||||
| 
 | 
 | ||||||
|     await service.addEnvironmentToProject('uniqueness-test', 'default'); |  | ||||||
|     return expect(async () => |     return expect(async () => | ||||||
|         service.addEnvironmentToProject('uniqueness-test', 'default'), |         service.addEnvironmentToProject('uniqueness-test', 'default'), | ||||||
|     ).rejects.toThrow( |     ).rejects.toThrow( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user