1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-19 17:52:45 +02:00

chore: add release plans/templates count to instance stats (#10540)

This commit is contained in:
David Leek 2025-08-28 14:44:24 +02:00 committed by GitHub
parent e355506bb9
commit 7fb523e348
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 96 additions and 5 deletions

View File

@ -63,6 +63,8 @@ export const InstanceStats: FC = () => {
title: 'Highest number of values used for a single constraint',
value: stats?.maxConstraintValues,
},
{ title: 'Release templates', value: stats?.releaseTemplates },
{ title: 'Release plans', value: stats?.releasePlans },
];
if (stats?.versionEnterprise) {

View File

@ -82,6 +82,16 @@ export interface InstanceAdminStatsSchema {
* @minimum 0
*/
projects?: number;
/**
* The number of release plans in this instance
* @minimum 0
*/
releasePlans?: number;
/**
* The number of release templates in this instance
* @minimum 0
*/
releaseTemplates?: number;
/**
* The number of roles defined in this instance
* @minimum 0

View File

@ -48,6 +48,8 @@ import {
createFakeGetLicensedUsers,
createGetLicensedUsers,
} from './getLicensedUsers.js';
import { ReleasePlanStore } from '../release-plans/release-plan-store.js';
import { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
const { eventBus, getLogger, flagResolver } = config;
@ -104,6 +106,9 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
getLogger,
flagResolver,
);
const releasePlanTemplateStore = new ReleasePlanTemplateStore(db, config);
const releasePlanStore = new ReleasePlanStore(db, config);
const instanceStatsServiceStores = {
featureToggleStore,
userStore,
@ -122,6 +127,8 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
featureStrategiesReadModel,
featureStrategiesStore,
trafficDataUsageStore,
releasePlanTemplateStore,
releasePlanStore,
};
const versionServiceStores = { settingStore };
const getActiveUsers = createGetActiveUsers(db);
@ -160,6 +167,12 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
const featureStrategiesReadModel = new FakeFeatureStrategiesReadModel();
const trafficDataUsageStore = new FakeTrafficDataUsageStore();
const featureStrategiesStore = new FakeFeatureStrategiesStore();
const releasePlanTemplateStore = {
count: () => Promise.resolve(0),
} as ReleasePlanTemplateStore;
const releasePlanStore = {
count: () => Promise.resolve(0),
} as ReleasePlanStore;
const instanceStatsServiceStores = {
featureToggleStore,
userStore,
@ -178,6 +191,8 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
featureStrategiesReadModel,
featureStrategiesStore,
trafficDataUsageStore,
releasePlanTemplateStore,
releasePlanStore,
};
const versionServiceStores = { settingStore };

View File

@ -31,6 +31,8 @@ import { format, minutesToMilliseconds } from 'date-fns';
import memoizee from 'memoizee';
import type { GetLicensedUsers } from './getLicensedUsers.js';
import type { IFeatureUsageInfo } from '../../services/version-service.js';
import type { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
import type { ReleasePlanStore } from '../release-plans/release-plan-store.js';
export type TimeRange = 'allTime' | '30d' | '7d';
@ -70,6 +72,8 @@ export interface InstanceStats {
maxEnvironmentStrategies: number;
maxConstraints: number;
maxConstraintValues: number;
releaseTemplates?: number;
releasePlans?: number;
}
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
@ -126,6 +130,10 @@ export class InstanceStatsService {
private trafficDataUsageStore: ITrafficDataUsageStore;
private releasePlanTemplateStore: ReleasePlanTemplateStore;
private releasePlanStore: ReleasePlanStore;
constructor(
{
featureToggleStore,
@ -145,6 +153,8 @@ export class InstanceStatsService {
featureStrategiesReadModel,
featureStrategiesStore,
trafficDataUsageStore,
releasePlanTemplateStore,
releasePlanStore,
}: Pick<
IUnleashStores,
| 'featureToggleStore'
@ -164,6 +174,8 @@ export class InstanceStatsService {
| 'featureStrategiesReadModel'
| 'featureStrategiesStore'
| 'trafficDataUsageStore'
| 'releasePlanTemplateStore'
| 'releasePlanStore'
>,
{
getLogger,
@ -203,6 +215,8 @@ export class InstanceStatsService {
this.featureStrategiesReadModel = featureStrategiesReadModel;
this.featureStrategiesStore = featureStrategiesStore;
this.trafficDataUsageStore = trafficDataUsageStore;
this.releasePlanTemplateStore = releasePlanTemplateStore;
this.releasePlanStore = releasePlanStore;
}
memory = new Map<string, () => Promise<any>>();
@ -295,6 +309,20 @@ export class InstanceStatsService {
});
}
async getReleaseTemplates(): Promise<number> {
return this.memorize('getReleaseTemplates', async () => {
const count = await this.releasePlanTemplateStore.count();
return count;
});
}
async getReleasePlans(): Promise<number> {
return this.memorize('getReleasePlans', async () => {
const count = await this.releasePlanStore.count();
return count;
});
}
async getStats(): Promise<InstanceStats> {
const versionInfo = await this.versionService.getVersionInfo();
const [
@ -326,6 +354,8 @@ export class InstanceStatsService {
maxEnvironmentStrategies,
maxConstraintValues,
maxConstraints,
releaseTemplates,
releasePlans,
] = await Promise.all([
this.getToggleCount(),
this.getArchivedToggleCount(),
@ -370,6 +400,8 @@ export class InstanceStatsService {
this.featureStrategiesReadModel,
),
),
this.getReleaseTemplates(),
this.getReleasePlans(),
]);
return {
@ -408,6 +440,8 @@ export class InstanceStatsService {
maxEnvironmentStrategies: maxEnvironmentStrategies?.count ?? 0,
maxConstraintValues: maxConstraintValues?.count ?? 0,
maxConstraints: maxConstraints?.count ?? 0,
releaseTemplates,
releasePlans,
};
}
@ -435,6 +469,8 @@ export class InstanceStatsService {
postgresVersion,
licenseType,
hostedBy,
releaseTemplates,
releasePlans,
] = await Promise.all([
this.getToggleCount(),
this.getRegisteredUsers(),
@ -458,6 +494,8 @@ export class InstanceStatsService {
this.postgresVersion(),
this.getLicenseType(),
this.getHostedBy(),
this.getReleaseTemplates(),
this.getReleasePlans(),
]);
const versionInfo = await this.versionService.getVersionInfo();
@ -493,6 +531,8 @@ export class InstanceStatsService {
postgresVersion,
licenseType,
hostedBy,
releaseTemplates,
releasePlans,
};
return featureInfo;
}
@ -663,7 +703,7 @@ export class InstanceStatsService {
.reduce((a, b) => a + b, 0);
const sum = sha256(
`${instanceStats.instanceId}${instanceStats.users}${instanceStats.featureToggles}${totalProjects}${instanceStats.roles}${instanceStats.groups}${instanceStats.environments}${instanceStats.segments}`,
`${instanceStats.instanceId}${instanceStats.users}${instanceStats.featureToggles}${totalProjects}${instanceStats.roles}${instanceStats.groups}${instanceStats.environments}${instanceStats.segments}${instanceStats.releaseTemplates}${instanceStats.releasePlans}`,
);
return { ...instanceStats, sum, projects: totalProjects };
}

View File

@ -272,6 +272,18 @@ export const instanceAdminStatsSchema = {
description:
'The highest number of constraint values used on a single constraint.',
},
releaseTemplates: {
type: 'integer',
minimum: 0,
example: 2,
description: 'The number of release templates in this instance',
},
releasePlans: {
type: 'integer',
minimum: 0,
example: 1,
description: 'The number of release plans in this instance',
},
sum: {
type: 'string',
description:

View File

@ -131,6 +131,8 @@ class InstanceAdminController extends Controller {
maxEnvironmentStrategies: 20,
maxConstraints: 17,
maxConstraintValues: 123,
releaseTemplates: 3,
releasePlans: 5,
};
}

View File

@ -43,6 +43,8 @@ const fakeTelemetryData = {
postgresVersion: '17.1 (Debian 17.1-1.pgdg120+1)',
licenseType: 'test',
hostedBy: 'self-hosted',
releaseTemplates: 2,
releasePlans: 4,
};
test('yields current versions', async () => {

View File

@ -52,6 +52,8 @@ export interface IFeatureUsageInfo {
postgresVersion: string;
licenseType: string;
hostedBy: string;
releaseTemplates: number;
releasePlans: number;
}
export default class VersionService {

View File

@ -124,7 +124,7 @@ test('should return signed instance statistics', async () => {
.expect((res) => {
expect(res.body.instanceId).toBe('test-static');
expect(res.body.sum).toBe(
'd9bac94bba7afa20d98f0a9d54a84b79a6668f8103b8f89db85d05d38e84f519',
'a5fd70e5ba5dfa02644404d4d075cb7f783487f607fbc00e8e4bc0aef41fd81a',
);
});
});

View File

@ -137,9 +137,15 @@ const createStores: () => IUnleashStores = () => {
uniqueConnectionReadModel: new UniqueConnectionReadModel(
uniqueConnectionStore,
),
releasePlanStore: {} as ReleasePlanStore,
releasePlanMilestoneStore: {} as ReleasePlanMilestoneStore,
releasePlanTemplateStore: {} as ReleasePlanTemplateStore,
releasePlanStore: {
count: () => Promise.resolve(0),
} as ReleasePlanStore,
releasePlanMilestoneStore: {
count: () => Promise.resolve(0),
} as ReleasePlanMilestoneStore,
releasePlanTemplateStore: {
count: () => Promise.resolve(0),
} as ReleasePlanTemplateStore,
releasePlanMilestoneStrategyStore:
{} as ReleasePlanMilestoneStrategyStore,
featureLinkStore: new FakeFeatureLinkStore(),