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, |     IFlagResolver, | ||||||
|     IProject, |     IProject, | ||||||
|     IProjectWithCount, |     IProjectWithCount, | ||||||
|  |     ProjectMode, | ||||||
| } from '../types'; | } from '../types'; | ||||||
| import { | import { | ||||||
|     IProjectHealthUpdate, |     IProjectHealthUpdate, | ||||||
| @ -49,6 +50,11 @@ export interface IEnvironmentProjectLink { | |||||||
|     projectId: string; |     projectId: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export interface ProjectModeCount { | ||||||
|  |     mode: ProjectMode; | ||||||
|  |     count: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export interface IProjectMembersCount { | export interface IProjectMembersCount { | ||||||
|     count: number; |     count: number; | ||||||
|     project: string; |     project: string; | ||||||
| @ -551,6 +557,34 @@ class ProjectStore implements IProjectStore { | |||||||
|             .then((res) => Number(res[0].count)); |             .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
 |     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||||
|     mapLinkRow(row): IEnvironmentProjectLink { |     mapLinkRow(row): IEnvironmentProjectLink { | ||||||
|         return { |         return { | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import { ISettingStore } from '../../types/stores/settings-store'; | |||||||
| import { FEATURES_EXPORTED, FEATURES_IMPORTED } from '../../types'; | import { FEATURES_EXPORTED, FEATURES_IMPORTED } from '../../types'; | ||||||
| import { CUSTOM_ROOT_ROLE_TYPE } from '../../util'; | import { CUSTOM_ROOT_ROLE_TYPE } from '../../util'; | ||||||
| import { type GetActiveUsers } from './getActiveUsers'; | import { type GetActiveUsers } from './getActiveUsers'; | ||||||
|  | import { ProjectModeCount } from '../../db/project-store'; | ||||||
| 
 | 
 | ||||||
| export type TimeRange = 'allTime' | '30d' | '7d'; | export type TimeRange = 'allTime' | '30d' | '7d'; | ||||||
| 
 | 
 | ||||||
| @ -30,7 +31,7 @@ export interface InstanceStats { | |||||||
|     versionEnterprise?: string; |     versionEnterprise?: string; | ||||||
|     users: number; |     users: number; | ||||||
|     featureToggles: number; |     featureToggles: number; | ||||||
|     projects: number; |     projects: ProjectModeCount[]; | ||||||
|     contextFields: number; |     contextFields: number; | ||||||
|     roles: number; |     roles: number; | ||||||
|     customRootRoles: number; |     customRootRoles: number; | ||||||
| @ -47,9 +48,10 @@ export interface InstanceStats { | |||||||
|     activeUsers: Awaited<ReturnType<GetActiveUsers>>; |     activeUsers: Awaited<ReturnType<GetActiveUsers>>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface InstanceStatsSigned extends InstanceStats { | export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & { | ||||||
|  |     projects: number; | ||||||
|     sum: string; |     sum: string; | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| export class InstanceStatsService { | export class InstanceStatsService { | ||||||
|     private logger: Logger; |     private logger: Logger; | ||||||
| @ -152,6 +154,10 @@ export class InstanceStatsService { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     getProjectModeCount(): Promise<ProjectModeCount[]> { | ||||||
|  |         return this.projectStore.getProjectModeCounts(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     getToggleCount(): Promise<number> { |     getToggleCount(): Promise<number> { | ||||||
|         return this.featureToggleStore.count({ |         return this.featureToggleStore.count({ | ||||||
|             archived: false, |             archived: false, | ||||||
| @ -201,7 +207,7 @@ export class InstanceStatsService { | |||||||
|             this.getToggleCount(), |             this.getToggleCount(), | ||||||
|             this.userStore.count(), |             this.userStore.count(), | ||||||
|             this.getActiveUsers(), |             this.getActiveUsers(), | ||||||
|             this.projectStore.count(), |             this.getProjectModeCount(), | ||||||
|             this.contextFieldStore.count(), |             this.contextFieldStore.count(), | ||||||
|             this.groupStore.count(), |             this.groupStore.count(), | ||||||
|             this.roleStore.count(), |             this.roleStore.count(), | ||||||
| @ -275,10 +281,13 @@ export class InstanceStatsService { | |||||||
| 
 | 
 | ||||||
|     async getSignedStats(): Promise<InstanceStatsSigned> { |     async getSignedStats(): Promise<InstanceStatsSigned> { | ||||||
|         const instanceStats = await this.getStats(); |         const instanceStats = await this.getStats(); | ||||||
|  |         const totalProjects = instanceStats.projects | ||||||
|  |             .map((p) => p.count) | ||||||
|  |             .reduce((a, b) => a + b, 0); | ||||||
| 
 | 
 | ||||||
|         const sum = sha256( |         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({ |         const projectsTotal = new client.Gauge({ | ||||||
|             name: 'projects_total', |             name: 'projects_total', | ||||||
|             help: 'Number of projects', |             help: 'Number of projects', | ||||||
|  |             labelNames: ['mode'], | ||||||
|         }); |         }); | ||||||
|         const environmentsTotal = new client.Gauge({ |         const environmentsTotal = new client.Gauge({ | ||||||
|             name: 'environments_total', |             name: 'environments_total', | ||||||
| @ -193,7 +194,11 @@ export default class MetricsMonitor { | |||||||
|                 usersActive90days.set(stats.activeUsers.last90); |                 usersActive90days.set(stats.activeUsers.last90); | ||||||
| 
 | 
 | ||||||
|                 projectsTotal.reset(); |                 projectsTotal.reset(); | ||||||
|                 projectsTotal.set(stats.projects); |                 stats.projects.forEach((projectStat) => { | ||||||
|  |                     projectsTotal | ||||||
|  |                         .labels({ mode: projectStat.mode }) | ||||||
|  |                         .set(projectStat.count); | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 environmentsTotal.reset(); |                 environmentsTotal.reset(); | ||||||
|                 environmentsTotal.set(stats.environments); |                 environmentsTotal.set(stats.environments); | ||||||
|  | |||||||
| @ -7,7 +7,6 @@ import Controller from '../controller'; | |||||||
| import { NONE } from '../../types/permissions'; | import { NONE } from '../../types/permissions'; | ||||||
| import { UiConfigSchema } from '../../openapi/spec/ui-config-schema'; | import { UiConfigSchema } from '../../openapi/spec/ui-config-schema'; | ||||||
| import { | import { | ||||||
|     InstanceStats, |  | ||||||
|     InstanceStatsService, |     InstanceStatsService, | ||||||
|     InstanceStatsSigned, |     InstanceStatsSigned, | ||||||
| } from '../../features/instance-stats/instance-stats-service'; | } from '../../features/instance-stats/instance-stats-service'; | ||||||
| @ -97,7 +96,7 @@ class InstanceAdminController extends Controller { | |||||||
|             featureToggles: 29, |             featureToggles: 29, | ||||||
|             groups: 3, |             groups: 3, | ||||||
|             instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b', |             instanceId: 'ed3861ae-78f9-4e8c-8e57-b57efc15f82b', | ||||||
|             projects: 1, |             projects: 4, | ||||||
|             roles: 5, |             roles: 5, | ||||||
|             customRootRoles: 2, |             customRootRoles: 2, | ||||||
|             customRootRolesInUse: 1, |             customRootRolesInUse: 1, | ||||||
| @ -119,7 +118,7 @@ class InstanceAdminController extends Controller { | |||||||
| 
 | 
 | ||||||
|     async getStatistics( |     async getStatistics( | ||||||
|         req: AuthedRequest, |         req: AuthedRequest, | ||||||
|         res: Response<InstanceStats>, |         res: Response<InstanceStatsSigned>, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         const instanceStats = await this.instanceStatsService.getSignedStats(); |         const instanceStats = await this.instanceStatsService.getSignedStats(); | ||||||
|         res.json(instanceStats); |         res.json(instanceStats); | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { | import { | ||||||
|     IEnvironmentProjectLink, |     IEnvironmentProjectLink, | ||||||
|     IProjectMembersCount, |     IProjectMembersCount, | ||||||
|  |     ProjectModeCount, | ||||||
| } from '../../db/project-store'; | } from '../../db/project-store'; | ||||||
| import { | import { | ||||||
|     IEnvironment, |     IEnvironment, | ||||||
| @ -32,21 +33,6 @@ export interface IProjectSettings { | |||||||
|     featureNamingDescription?: string; |     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 { | export interface IProjectHealthUpdate { | ||||||
|     id: string; |     id: string; | ||||||
|     health: number; |     health: number; | ||||||
| @ -115,6 +101,7 @@ export interface IProjectStore extends Store<IProject, string> { | |||||||
|         projectId: string, |         projectId: string, | ||||||
|         environment: string, |         environment: string, | ||||||
|     ): Promise<CreateFeatureStrategySchema | null>; |     ): Promise<CreateFeatureStrategySchema | null>; | ||||||
|  | 
 | ||||||
|     updateDefaultStrategy( |     updateDefaultStrategy( | ||||||
|         projectId: string, |         projectId: string, | ||||||
|         environment: string, |         environment: string, | ||||||
| @ -122,4 +109,6 @@ export interface IProjectStore extends Store<IProject, string> { | |||||||
|     ): Promise<CreateFeatureStrategySchema>; |     ): Promise<CreateFeatureStrategySchema>; | ||||||
| 
 | 
 | ||||||
|     isFeatureLimitReached(id: string): Promise<boolean>; |     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 { | import { | ||||||
|     IEnvironmentProjectLink, |     IEnvironmentProjectLink, | ||||||
|     IProjectMembersCount, |     IProjectMembersCount, | ||||||
|  |     ProjectModeCount, | ||||||
| } from 'lib/db/project-store'; | } from 'lib/db/project-store'; | ||||||
| import { CreateFeatureStrategySchema } from '../../lib/openapi'; | import { CreateFeatureStrategySchema } from '../../lib/openapi'; | ||||||
| 
 | 
 | ||||||
| @ -185,4 +186,8 @@ export default class FakeProjectStore implements IProjectStore { | |||||||
|     isFeatureLimitReached(id: string): Promise<boolean> { |     isFeatureLimitReached(id: string): Promise<boolean> { | ||||||
|         return Promise.resolve(false); |         return Promise.resolve(false); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     getProjectModeCounts(): Promise<ProjectModeCount[]> { | ||||||
|  |         return Promise.resolve([]); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user