mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: application missing features backend (#6330)
This PR adds a property issues to application schema, and also adds all the missing features that have been reported by SDK, but do not exist in Unleash.
This commit is contained in:
		
							parent
							
								
									d1e93228a3
								
							
						
					
					
						commit
						89d113f1ff
					
				| @ -10,6 +10,7 @@ import { Logger, LogProvider } from '../logger'; | ||||
| import { Db } from './db'; | ||||
| import { IApplicationOverview } from '../features/metrics/instance/models'; | ||||
| import { applySearchFilters } from '../features/feature-search/search-utils'; | ||||
| import { ApplicationOverviewIssuesSchema } from '../openapi/spec/application-overview-issues-schema'; | ||||
| 
 | ||||
| const COLUMNS = [ | ||||
|     'app_name', | ||||
| @ -305,7 +306,6 @@ export default class ClientApplicationsStore | ||||
|                 ); | ||||
|             }) | ||||
|             .where('a.app_name', appName); | ||||
| 
 | ||||
|         const rows = await query; | ||||
|         if (!rows.length) { | ||||
|             throw new NotFoundError(`Could not find appName=${appName}`); | ||||
| @ -316,22 +316,41 @@ export default class ClientApplicationsStore | ||||
| 
 | ||||
|     mapApplicationOverviewData(rows: any[]): IApplicationOverview { | ||||
|         const featureCount = new Set(rows.map((row) => row.feature_name)).size; | ||||
|         const missingFeatures: Set<string> = new Set(); | ||||
| 
 | ||||
|         const environments = rows.reduce((acc, row) => { | ||||
|             const { environment, instance_id, sdk_version, last_seen } = row; | ||||
|             const { | ||||
|                 environment, | ||||
|                 instance_id, | ||||
|                 sdk_version, | ||||
|                 last_seen, | ||||
|                 project, | ||||
|                 feature_name, | ||||
|             } = row; | ||||
| 
 | ||||
|             if (!environment) return acc; | ||||
| 
 | ||||
|             if (!project && feature_name) { | ||||
|                 missingFeatures.add(feature_name); | ||||
|             } | ||||
| 
 | ||||
|             let env = acc.find((e) => e.name === environment); | ||||
|             if (!env) { | ||||
|                 env = { | ||||
|                     name: environment, | ||||
|                     instanceCount: 1, | ||||
|                     instanceCount: instance_id ? 1 : 0, | ||||
|                     sdks: sdk_version ? [sdk_version] : [], | ||||
|                     lastSeen: last_seen, | ||||
|                     uniqueInstanceIds: new Set([instance_id]), | ||||
|                     uniqueInstanceIds: new Set( | ||||
|                         instance_id ? [instance_id] : [], | ||||
|                     ), | ||||
|                 }; | ||||
|                 acc.push(env); | ||||
|             } else { | ||||
|                 env.uniqueInstanceIds.add(instance_id); | ||||
|                 env.instanceCount = env.uniqueInstanceIds.size; | ||||
|                 if (instance_id && !env.uniqueInstanceIds.has(instance_id)) { | ||||
|                     env.uniqueInstanceIds.add(instance_id); | ||||
|                     env.instanceCount = env.uniqueInstanceIds.size; | ||||
|                 } | ||||
|                 if (sdk_version && !env.sdks.includes(sdk_version)) { | ||||
|                     env.sdks.push(sdk_version); | ||||
|                 } | ||||
| @ -342,12 +361,21 @@ export default class ClientApplicationsStore | ||||
| 
 | ||||
|             return acc; | ||||
|         }, []); | ||||
| 
 | ||||
|         environments.forEach((env) => { | ||||
|             delete env.uniqueInstanceIds; | ||||
|             env.sdks.sort(); | ||||
|         }); | ||||
| 
 | ||||
|         const issues: ApplicationOverviewIssuesSchema[] = | ||||
|             missingFeatures.size > 0 | ||||
|                 ? [ | ||||
|                       { | ||||
|                           type: 'missingFeatures', | ||||
|                           items: [...missingFeatures], | ||||
|                       }, | ||||
|                   ] | ||||
|                 : []; | ||||
| 
 | ||||
|         return { | ||||
|             projects: [ | ||||
|                 ...new Set( | ||||
| @ -358,6 +386,7 @@ export default class ClientApplicationsStore | ||||
|             ], | ||||
|             featureCount, | ||||
|             environments, | ||||
|             issues, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ export const applicationOverviewIssuesSchema = { | ||||
|     properties: { | ||||
|         type: { | ||||
|             type: 'string', | ||||
|             enum: ['missingFeature', 'missingStrategy'], | ||||
|             enum: ['missingFeatures', 'missingStrategies'], | ||||
|             description: 'The name of this action.', | ||||
|         }, | ||||
|         items: { | ||||
|  | ||||
| @ -5,8 +5,8 @@ test('applicationOverviewSchema', () => { | ||||
|         projects: ['default', 'dx'], | ||||
|         featureCount: 12, | ||||
|         issues: [ | ||||
|             { type: 'missingFeature', items: ['feature1'] }, | ||||
|             { type: 'missingStrategy', items: ['strategy1'] }, | ||||
|             { type: 'missingFeatures', items: ['feature1'] }, | ||||
|             { type: 'missingStrategies', items: ['strategy1'] }, | ||||
|         ], | ||||
|         environments: [ | ||||
|             { | ||||
|  | ||||
| @ -8,7 +8,7 @@ export const applicationOverviewSchema = { | ||||
|     description: | ||||
|         "Data about an application that's connected to Unleash via an SDK.", | ||||
|     additionalProperties: false, | ||||
|     required: ['projects', 'featureCount', 'environments'], | ||||
|     required: ['projects', 'featureCount', 'environments', 'issues'], | ||||
|     properties: { | ||||
|         projects: { | ||||
|             description: 'The list of projects the application has been using.', | ||||
|  | ||||
| @ -122,6 +122,7 @@ test('should show correct number of total', async () => { | ||||
| 
 | ||||
|     const expected = { | ||||
|         projects: ['default'], | ||||
|         issues: [], | ||||
|         environments: [ | ||||
|             { | ||||
|                 instanceCount: 2, | ||||
| @ -134,3 +135,49 @@ test('should show correct number of total', async () => { | ||||
| 
 | ||||
|     expect(body).toMatchObject(expected); | ||||
| }); | ||||
| 
 | ||||
| test('should show missing features', async () => { | ||||
|     await Promise.all([ | ||||
|         app.createFeature('toggle-name-1'), | ||||
|         app.request.post('/api/client/register').send({ | ||||
|             appName: metrics.appName, | ||||
|             instanceId: metrics.instanceId, | ||||
|             strategies: ['default'], | ||||
|             sdkVersion: 'unleash-client-test', | ||||
|             started: Date.now(), | ||||
|             interval: 10, | ||||
|         }), | ||||
|     ]); | ||||
|     await app.services.clientInstanceService.bulkAdd(); | ||||
|     await app.request | ||||
|         .post('/api/client/metrics') | ||||
|         .set('Authorization', defaultToken.secret) | ||||
|         .send(metrics) | ||||
|         .expect(202); | ||||
| 
 | ||||
|     await app.services.clientMetricsServiceV2.bulkAdd(); | ||||
| 
 | ||||
|     const { body } = await app.request | ||||
|         .get(`/api/admin/metrics/applications/${metrics.appName}/overview`) | ||||
|         .expect(200); | ||||
| 
 | ||||
|     const expected = { | ||||
|         projects: ['default'], | ||||
|         issues: [ | ||||
|             { | ||||
|                 type: 'missingFeatures', | ||||
|                 items: ['toggle-name-2', 'toggle-name-3'], | ||||
|             }, | ||||
|         ], | ||||
|         environments: [ | ||||
|             { | ||||
|                 instanceCount: 1, | ||||
|                 name: 'default', | ||||
|                 sdks: ['unleash-client-test'], | ||||
|             }, | ||||
|         ], | ||||
|         featureCount: 3, | ||||
|     }; | ||||
| 
 | ||||
|     expect(body).toMatchObject(expected); | ||||
| }); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user