mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add project collaboration mode to prometheus (#4819)
This commit is contained in:
		
							parent
							
								
									9041422cc9
								
							
						
					
					
						commit
						f7d87a2339
					
				| @ -7,6 +7,7 @@ import { | ||||
|     IFlagResolver, | ||||
|     IProject, | ||||
|     IProjectWithCount, | ||||
|     ProjectMode, | ||||
| } from '../types'; | ||||
| import { | ||||
|     IProjectHealthUpdate, | ||||
| @ -49,6 +50,11 @@ export interface IEnvironmentProjectLink { | ||||
|     projectId: string; | ||||
| } | ||||
| 
 | ||||
| export interface ProjectModeCount { | ||||
|     mode: ProjectMode; | ||||
|     count: number; | ||||
| } | ||||
| 
 | ||||
| export interface IProjectMembersCount { | ||||
|     count: number; | ||||
|     project: string; | ||||
| @ -551,6 +557,34 @@ class ProjectStore implements IProjectStore { | ||||
|             .then((res) => Number(res[0].count)); | ||||
|     } | ||||
| 
 | ||||
|     async getProjectModeCounts(): Promise<ProjectModeCount[]> { | ||||
|         const result: ProjectModeCount[] = await this.db | ||||
|             .select( | ||||
|                 this.db.raw( | ||||
|                     `COALESCE(${SETTINGS_TABLE}.project_mode, 'open') as mode`, | ||||
|                 ), | ||||
|             ) | ||||
|             .count(`${TABLE}.id as count`) | ||||
|             .from(`${TABLE}`) | ||||
|             .join( | ||||
|                 `${SETTINGS_TABLE}`, | ||||
|                 `${TABLE}.id`, | ||||
|                 `${SETTINGS_TABLE}.project`, | ||||
|             ) | ||||
|             .groupBy( | ||||
|                 this.db.raw(`COALESCE(${SETTINGS_TABLE}.project_mode, 'open')`), | ||||
|             ); | ||||
|         return result.map(this.mapProjectModeCount); | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     mapProjectModeCount(row): ProjectModeCount { | ||||
|         return { | ||||
|             mode: row.mode, | ||||
|             count: Number(row.count), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     mapLinkRow(row): IEnvironmentProjectLink { | ||||
|         return { | ||||
|  | ||||
| @ -20,6 +20,7 @@ import { ISettingStore } from '../../types/stores/settings-store'; | ||||
| import { FEATURES_EXPORTED, FEATURES_IMPORTED } from '../../types'; | ||||
| import { CUSTOM_ROOT_ROLE_TYPE } from '../../util'; | ||||
| import { type GetActiveUsers } from './getActiveUsers'; | ||||
| import { ProjectModeCount } from '../../db/project-store'; | ||||
| 
 | ||||
| export type TimeRange = 'allTime' | '30d' | '7d'; | ||||
| 
 | ||||
| @ -30,7 +31,7 @@ export interface InstanceStats { | ||||
|     versionEnterprise?: string; | ||||
|     users: number; | ||||
|     featureToggles: number; | ||||
|     projects: number; | ||||
|     projects: ProjectModeCount[]; | ||||
|     contextFields: number; | ||||
|     roles: number; | ||||
|     customRootRoles: number; | ||||
| @ -47,9 +48,10 @@ export interface InstanceStats { | ||||
|     activeUsers: Awaited<ReturnType<GetActiveUsers>>; | ||||
| } | ||||
| 
 | ||||
| export interface InstanceStatsSigned extends InstanceStats { | ||||
| export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & { | ||||
|     projects: number; | ||||
|     sum: string; | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| export class InstanceStatsService { | ||||
|     private logger: Logger; | ||||
| @ -152,6 +154,10 @@ export class InstanceStatsService { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getProjectModeCount(): Promise<ProjectModeCount[]> { | ||||
|         return this.projectStore.getProjectModeCounts(); | ||||
|     } | ||||
| 
 | ||||
|     getToggleCount(): Promise<number> { | ||||
|         return this.featureToggleStore.count({ | ||||
|             archived: false, | ||||
| @ -201,7 +207,7 @@ export class InstanceStatsService { | ||||
|             this.getToggleCount(), | ||||
|             this.userStore.count(), | ||||
|             this.getActiveUsers(), | ||||
|             this.projectStore.count(), | ||||
|             this.getProjectModeCount(), | ||||
|             this.contextFieldStore.count(), | ||||
|             this.groupStore.count(), | ||||
|             this.roleStore.count(), | ||||
| @ -275,10 +281,13 @@ export class InstanceStatsService { | ||||
| 
 | ||||
|     async getSignedStats(): Promise<InstanceStatsSigned> { | ||||
|         const instanceStats = await this.getStats(); | ||||
|         const totalProjects = instanceStats.projects | ||||
|             .map((p) => p.count) | ||||
|             .reduce((a, b) => a + b, 0); | ||||
| 
 | ||||
|         const sum = sha256( | ||||
|             `${instanceStats.instanceId}${instanceStats.users}${instanceStats.featureToggles}${instanceStats.projects}${instanceStats.roles}${instanceStats.groups}${instanceStats.environments}${instanceStats.segments}`, | ||||
|             `${instanceStats.instanceId}${instanceStats.users}${instanceStats.featureToggles}${totalProjects}${instanceStats.roles}${instanceStats.groups}${instanceStats.environments}${instanceStats.segments}`, | ||||
|         ); | ||||
|         return { ...instanceStats, sum }; | ||||
|         return { ...instanceStats, sum, projects: totalProjects }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -105,6 +105,7 @@ export default class MetricsMonitor { | ||||
|         const projectsTotal = new client.Gauge({ | ||||
|             name: 'projects_total', | ||||
|             help: 'Number of projects', | ||||
|             labelNames: ['mode'], | ||||
|         }); | ||||
|         const environmentsTotal = new client.Gauge({ | ||||
|             name: 'environments_total', | ||||
| @ -193,7 +194,11 @@ export default class MetricsMonitor { | ||||
|                 usersActive90days.set(stats.activeUsers.last90); | ||||
| 
 | ||||
|                 projectsTotal.reset(); | ||||
|                 projectsTotal.set(stats.projects); | ||||
|                 stats.projects.forEach((projectStat) => { | ||||
|                     projectsTotal | ||||
|                         .labels({ mode: projectStat.mode }) | ||||
|                         .set(projectStat.count); | ||||
|                 }); | ||||
| 
 | ||||
|                 environmentsTotal.reset(); | ||||
|                 environmentsTotal.set(stats.environments); | ||||
|  | ||||
| @ -7,7 +7,6 @@ import Controller from '../controller'; | ||||
| import { NONE } from '../../types/permissions'; | ||||
| import { UiConfigSchema } from '../../openapi/spec/ui-config-schema'; | ||||
| import { | ||||
|     InstanceStats, | ||||
|     InstanceStatsService, | ||||
|     InstanceStatsSigned, | ||||
| } from '../../features/instance-stats/instance-stats-service'; | ||||
| @ -97,7 +96,7 @@ class InstanceAdminController extends Controller { | ||||
|             featureToggles: 29, | ||||
|             groups: 3, | ||||
|             instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b', | ||||
|             projects: 1, | ||||
|             projects: 4, | ||||
|             roles: 5, | ||||
|             customRootRoles: 2, | ||||
|             customRootRolesInUse: 1, | ||||
| @ -119,7 +118,7 @@ class InstanceAdminController extends Controller { | ||||
| 
 | ||||
|     async getStatistics( | ||||
|         req: AuthedRequest, | ||||
|         res: Response<InstanceStats>, | ||||
|         res: Response<InstanceStatsSigned>, | ||||
|     ): Promise<void> { | ||||
|         const instanceStats = await this.instanceStatsService.getSignedStats(); | ||||
|         res.json(instanceStats); | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import { | ||||
|     IEnvironmentProjectLink, | ||||
|     IProjectMembersCount, | ||||
|     ProjectModeCount, | ||||
| } from '../../db/project-store'; | ||||
| import { | ||||
|     IEnvironment, | ||||
| @ -32,21 +33,6 @@ export interface IProjectSettings { | ||||
|     featureNamingDescription?: string; | ||||
| } | ||||
| 
 | ||||
| export interface IProjectSettingsRow { | ||||
|     project_mode: ProjectMode; | ||||
|     default_stickiness: string; | ||||
| } | ||||
| 
 | ||||
| export interface IProjectEnvironmenDefaultStrategyRow { | ||||
|     environment: string; | ||||
|     default_strategy: any; | ||||
| } | ||||
| 
 | ||||
| export interface IProjectArchived { | ||||
|     id: string; | ||||
|     archived: boolean; | ||||
| } | ||||
| 
 | ||||
| export interface IProjectHealthUpdate { | ||||
|     id: string; | ||||
|     health: number; | ||||
| @ -115,6 +101,7 @@ export interface IProjectStore extends Store<IProject, string> { | ||||
|         projectId: string, | ||||
|         environment: string, | ||||
|     ): Promise<CreateFeatureStrategySchema | null>; | ||||
| 
 | ||||
|     updateDefaultStrategy( | ||||
|         projectId: string, | ||||
|         environment: string, | ||||
| @ -122,4 +109,6 @@ export interface IProjectStore extends Store<IProject, string> { | ||||
|     ): Promise<CreateFeatureStrategySchema>; | ||||
| 
 | ||||
|     isFeatureLimitReached(id: string): Promise<boolean>; | ||||
| 
 | ||||
|     getProjectModeCounts(): Promise<ProjectModeCount[]>; | ||||
| } | ||||
|  | ||||
							
								
								
									
										5
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								src/test/fixtures/fake-project-store.ts
									
									
									
									
										vendored
									
									
								
							| @ -9,6 +9,7 @@ import NotFoundError from '../../lib/error/notfound-error'; | ||||
| import { | ||||
|     IEnvironmentProjectLink, | ||||
|     IProjectMembersCount, | ||||
|     ProjectModeCount, | ||||
| } from 'lib/db/project-store'; | ||||
| import { CreateFeatureStrategySchema } from '../../lib/openapi'; | ||||
| 
 | ||||
| @ -185,4 +186,8 @@ export default class FakeProjectStore implements IProjectStore { | ||||
|     isFeatureLimitReached(id: string): Promise<boolean> { | ||||
|         return Promise.resolve(false); | ||||
|     } | ||||
| 
 | ||||
|     getProjectModeCounts(): Promise<ProjectModeCount[]> { | ||||
|         return Promise.resolve([]); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user