mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: Remove applications (#635)
This commit is contained in:
		
							parent
							
								
									edf6d70116
								
							
						
					
					
						commit
						4a3c136167
					
				| @ -87,6 +87,12 @@ class ClientApplicationsDb { | |||||||
|         return mapRow(row); |         return mapRow(row); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async deleteApplication(appName) { | ||||||
|  |         return this.db(TABLE) | ||||||
|  |             .where('app_name', appName) | ||||||
|  |             .del(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Could also be done in SQL: |      * Could also be done in SQL: | ||||||
|      * (not sure if it is faster though) |      * (not sure if it is faster though) | ||||||
|  | |||||||
| @ -127,6 +127,12 @@ class ClientInstanceStore { | |||||||
| 
 | 
 | ||||||
|         return rows.map(mapRow); |         return rows.map(mapRow); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     async deleteForApplication(appName) { | ||||||
|  |         return this.db(TABLE) | ||||||
|  |             .where('app_name', appName) | ||||||
|  |             .del(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = ClientInstanceStore; | module.exports = ClientInstanceStore; | ||||||
|  | |||||||
| @ -32,6 +32,11 @@ class MetricsController extends Controller { | |||||||
|             this.createApplication, |             this.createApplication, | ||||||
|             UPDATE_APPLICATION, |             UPDATE_APPLICATION, | ||||||
|         ); |         ); | ||||||
|  |         this.delete( | ||||||
|  |             '/applications/:appName', | ||||||
|  |             this.deleteApplication, | ||||||
|  |             UPDATE_APPLICATION, | ||||||
|  |         ); | ||||||
|         this.get('/applications/', this.getApplications); |         this.get('/applications/', this.getApplications); | ||||||
|         this.get('/applications/:appName', this.getApplication); |         this.get('/applications/:appName', this.getApplication); | ||||||
|     } |     } | ||||||
| @ -76,6 +81,27 @@ class MetricsController extends Controller { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     async deleteApplication(req, res) { | ||||||
|  |         const { appName } = req.params; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             await this.clientApplicationsStore.getApplication(appName); | ||||||
|  |         } catch (e) { | ||||||
|  |             this.logger.error(e); | ||||||
|  |             res.status(409).end(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             await this.clientInstanceStore.deleteForApplication(appName); | ||||||
|  |             await this.clientApplicationsStore.deleteApplication(appName); | ||||||
|  |             res.status(200).end(); | ||||||
|  |         } catch (e) { | ||||||
|  |             this.logger.error(e); | ||||||
|  |             res.status(500).end(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     async createApplication(req, res) { |     async createApplication(req, res) { | ||||||
|         const input = { ...req.body, appName: req.params.appName }; |         const input = { ...req.body, appName: req.params.appName }; | ||||||
|         const { value: applicationData, error } = schema.validate(input); |         const { value: applicationData, error } = schema.validate(input); | ||||||
|  | |||||||
| @ -154,3 +154,27 @@ test('should store application details wihtout strategies', t => { | |||||||
|         .send({ appName, url: 'htto://asd.com' }) |         .send({ appName, url: 'htto://asd.com' }) | ||||||
|         .expect(202); |         .expect(202); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('should not delete unknown application', t => { | ||||||
|  |     t.plan(0); | ||||||
|  |     const { request, perms } = getSetup(); | ||||||
|  |     const appName = 'unknown'; | ||||||
|  |     perms.withPermissions(UPDATE_APPLICATION); | ||||||
|  | 
 | ||||||
|  |     return request | ||||||
|  |         .delete(`/api/admin/metrics/applications/${appName}`) | ||||||
|  |         .expect(409); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test('should delete application', t => { | ||||||
|  |     t.plan(0); | ||||||
|  |     const { request, stores, perms } = getSetup(); | ||||||
|  |     const appName = 'deletable-test'; | ||||||
|  | 
 | ||||||
|  |     perms.withPermissions(UPDATE_APPLICATION); | ||||||
|  |     stores.clientApplicationsStore.upsert({ appName }); | ||||||
|  | 
 | ||||||
|  |     return request | ||||||
|  |         .delete(`/api/admin/metrics/applications/${appName}`) | ||||||
|  |         .expect(200); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -22,61 +22,6 @@ test.afterEach(async () => { | |||||||
|     await reset(); |     await reset(); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test.serial('should register client', async t => { |  | ||||||
|     t.plan(0); |  | ||||||
|     const request = await setupApp(stores); |  | ||||||
|     return request |  | ||||||
|         .post('/api/client/register') |  | ||||||
|         .send({ |  | ||||||
|             appName: 'demo', |  | ||||||
|             instanceId: 'test', |  | ||||||
|             strategies: ['default'], |  | ||||||
|             started: Date.now(), |  | ||||||
|             interval: 10, |  | ||||||
|         }) |  | ||||||
|         .expect(202); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| test.serial('should allow client to register multiple times', async t => { |  | ||||||
|     t.plan(0); |  | ||||||
|     const request = await setupApp(stores); |  | ||||||
|     const clientRegistration = { |  | ||||||
|         appName: 'multipleRegistration', |  | ||||||
|         instanceId: 'test', |  | ||||||
|         strategies: ['default', 'another'], |  | ||||||
|         started: Date.now(), |  | ||||||
|         interval: 10, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     return request |  | ||||||
|         .post('/api/client/register') |  | ||||||
|         .send(clientRegistration) |  | ||||||
|         .expect(202) |  | ||||||
|         .then(() => |  | ||||||
|             request |  | ||||||
|                 .post('/api/client/register') |  | ||||||
|                 .send(clientRegistration) |  | ||||||
|                 .expect(202), |  | ||||||
|         ); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| test.serial('should accept client metrics', async t => { |  | ||||||
|     t.plan(0); |  | ||||||
|     const request = await setupApp(stores); |  | ||||||
|     return request |  | ||||||
|         .post('/api/client/metrics') |  | ||||||
|         .send({ |  | ||||||
|             appName: 'demo', |  | ||||||
|             instanceId: '1', |  | ||||||
|             bucket: { |  | ||||||
|                 start: Date.now(), |  | ||||||
|                 stop: Date.now(), |  | ||||||
|                 toggles: {}, |  | ||||||
|             }, |  | ||||||
|         }) |  | ||||||
|         .expect(202); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| test.serial('should get application details', async t => { | test.serial('should get application details', async t => { | ||||||
|     t.plan(3); |     t.plan(3); | ||||||
|     const request = await setupApp(stores); |     const request = await setupApp(stores); | ||||||
| @ -98,6 +43,32 @@ test.serial('should get list of applications', async t => { | |||||||
|         .expect('Content-Type', /json/) |         .expect('Content-Type', /json/) | ||||||
|         .expect(res => { |         .expect(res => { | ||||||
|             t.true(res.status === 200); |             t.true(res.status === 200); | ||||||
|  |             t.is(res.body.applications.length, 3); | ||||||
|  |         }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('should delete application', async t => { | ||||||
|  |     t.plan(2); | ||||||
|  |     const request = await setupApp(stores); | ||||||
|  |     await request | ||||||
|  |         .delete('/api/admin/metrics/applications/deletable-app') | ||||||
|  |         .expect(res => { | ||||||
|  |             t.is(res.status, 200); | ||||||
|  |         }); | ||||||
|  |     return request | ||||||
|  |         .get('/api/admin/metrics/applications') | ||||||
|  |         .expect('Content-Type', /json/) | ||||||
|  |         .expect(res => { | ||||||
|             t.is(res.body.applications.length, 2); |             t.is(res.body.applications.length, 2); | ||||||
|         }); |         }); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test.serial('should get 409 when deleting unknwn application', async t => { | ||||||
|  |     t.plan(1); | ||||||
|  |     const request = await setupApp(stores); | ||||||
|  |     return request | ||||||
|  |         .delete('/api/admin/metrics/applications/unkown') | ||||||
|  |         .expect(res => { | ||||||
|  |             t.is(res.status, 409); | ||||||
|  |         }); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -37,3 +37,58 @@ test.serial('should require valid send metrics', async t => { | |||||||
|         }) |         }) | ||||||
|         .expect(400); |         .expect(400); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test.serial('should register client', async t => { | ||||||
|  |     t.plan(0); | ||||||
|  |     const request = await setupApp(stores); | ||||||
|  |     return request | ||||||
|  |         .post('/api/client/register') | ||||||
|  |         .send({ | ||||||
|  |             appName: 'demo', | ||||||
|  |             instanceId: 'test', | ||||||
|  |             strategies: ['default'], | ||||||
|  |             started: Date.now(), | ||||||
|  |             interval: 10, | ||||||
|  |         }) | ||||||
|  |         .expect(202); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('should allow client to register multiple times', async t => { | ||||||
|  |     t.plan(0); | ||||||
|  |     const request = await setupApp(stores); | ||||||
|  |     const clientRegistration = { | ||||||
|  |         appName: 'multipleRegistration', | ||||||
|  |         instanceId: 'test', | ||||||
|  |         strategies: ['default', 'another'], | ||||||
|  |         started: Date.now(), | ||||||
|  |         interval: 10, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return request | ||||||
|  |         .post('/api/client/register') | ||||||
|  |         .send(clientRegistration) | ||||||
|  |         .expect(202) | ||||||
|  |         .then(() => | ||||||
|  |             request | ||||||
|  |                 .post('/api/client/register') | ||||||
|  |                 .send(clientRegistration) | ||||||
|  |                 .expect(202), | ||||||
|  |         ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | test.serial('should accept client metrics', async t => { | ||||||
|  |     t.plan(0); | ||||||
|  |     const request = await setupApp(stores); | ||||||
|  |     return request | ||||||
|  |         .post('/api/client/metrics') | ||||||
|  |         .send({ | ||||||
|  |             appName: 'demo', | ||||||
|  |             instanceId: '1', | ||||||
|  |             bucket: { | ||||||
|  |                 start: Date.now(), | ||||||
|  |                 stop: Date.now(), | ||||||
|  |                 toggles: {}, | ||||||
|  |             }, | ||||||
|  |         }) | ||||||
|  |         .expect(202); | ||||||
|  | }); | ||||||
|  | |||||||
| @ -30,6 +30,11 @@ | |||||||
|       "appName": "demo-app-2", |       "appName": "demo-app-2", | ||||||
|       "strategies": ["default", "extra"], |       "strategies": ["default", "extra"], | ||||||
|       "description": "hello" |       "description": "hello" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "appName": "deletable-app", | ||||||
|  |       "strategies": ["default"], | ||||||
|  |       "description": "Some desc" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "clientInstances": [ |   "clientInstances": [ | ||||||
| @ -46,6 +51,13 @@ | |||||||
|       "strategies": ["default"], |       "strategies": ["default"], | ||||||
|       "started": 1516026938494, |       "started": 1516026938494, | ||||||
|       "interval": 10 |       "interval": 10 | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "appName": "deletable-app", | ||||||
|  |       "instanceId": "inst-1", | ||||||
|  |       "strategies": ["default"], | ||||||
|  |       "started": 1516026938494, | ||||||
|  |       "interval": 10 | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "features": [ |   "features": [ | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								test/fixtures/fake-client-applications-store.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								test/fixtures/fake-client-applications-store.js
									
									
									
									
										vendored
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| module.exports = () => { | module.exports = () => { | ||||||
|     const apps = []; |     let apps = []; | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|         upsert: app => { |         upsert: app => { | ||||||
| @ -9,6 +9,15 @@ module.exports = () => { | |||||||
|             return Promise.resolve(); |             return Promise.resolve(); | ||||||
|         }, |         }, | ||||||
|         getApplications: () => Promise.resolve(apps), |         getApplications: () => Promise.resolve(apps), | ||||||
|         getApplication: appName => apps.filter(a => a.name === appName)[0], |         getApplication: appName => { | ||||||
|  |             const app = apps.filter(a => a.appName === appName)[0]; | ||||||
|  |             if (!app) { | ||||||
|  |                 throw new Error(`Could not find app=${appName}`); | ||||||
|  |             } | ||||||
|  |             return app; | ||||||
|  |         }, | ||||||
|  |         deleteApplication: appName => { | ||||||
|  |             apps = apps.filter(app => app.appName !== appName); | ||||||
|  |         }, | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								test/fixtures/fake-client-instance-store.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/fixtures/fake-client-instance-store.js
									
									
									
									
										vendored
									
									
								
							| @ -3,4 +3,5 @@ | |||||||
| module.exports = () => ({ | module.exports = () => ({ | ||||||
|     insert: () => Promise.resolve(), |     insert: () => Promise.resolve(), | ||||||
|     getApplications: () => Promise.resolve([]), |     getApplications: () => Promise.resolve([]), | ||||||
|  |     deleteForApplication: () => Promise.resolve(), | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user