1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: add project collaboration mode to prometheus (#4819)

This commit is contained in:
Jaanus Sellin 2023-09-25 12:07:59 +03:00 committed by GitHub
parent 9041422cc9
commit f7d87a2339
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 25 deletions

View File

@ -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 {

View File

@ -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 };
} }
} }

View File

@ -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);

View File

@ -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);

View File

@ -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[]>;
} }

View File

@ -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([]);
}
} }