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:
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