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:
parent
e355506bb9
commit
7fb523e348
@ -63,6 +63,8 @@ export const InstanceStats: FC = () => {
|
|||||||
title: 'Highest number of values used for a single constraint',
|
title: 'Highest number of values used for a single constraint',
|
||||||
value: stats?.maxConstraintValues,
|
value: stats?.maxConstraintValues,
|
||||||
},
|
},
|
||||||
|
{ title: 'Release templates', value: stats?.releaseTemplates },
|
||||||
|
{ title: 'Release plans', value: stats?.releasePlans },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (stats?.versionEnterprise) {
|
if (stats?.versionEnterprise) {
|
||||||
|
@ -82,6 +82,16 @@ export interface InstanceAdminStatsSchema {
|
|||||||
* @minimum 0
|
* @minimum 0
|
||||||
*/
|
*/
|
||||||
projects?: number;
|
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
|
* The number of roles defined in this instance
|
||||||
* @minimum 0
|
* @minimum 0
|
||||||
|
@ -48,6 +48,8 @@ import {
|
|||||||
createFakeGetLicensedUsers,
|
createFakeGetLicensedUsers,
|
||||||
createGetLicensedUsers,
|
createGetLicensedUsers,
|
||||||
} from './getLicensedUsers.js';
|
} 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) => {
|
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
||||||
const { eventBus, getLogger, flagResolver } = config;
|
const { eventBus, getLogger, flagResolver } = config;
|
||||||
@ -104,6 +106,9 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
getLogger,
|
getLogger,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const releasePlanTemplateStore = new ReleasePlanTemplateStore(db, config);
|
||||||
|
const releasePlanStore = new ReleasePlanStore(db, config);
|
||||||
const instanceStatsServiceStores = {
|
const instanceStatsServiceStores = {
|
||||||
featureToggleStore,
|
featureToggleStore,
|
||||||
userStore,
|
userStore,
|
||||||
@ -122,6 +127,8 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
featureStrategiesReadModel,
|
featureStrategiesReadModel,
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
trafficDataUsageStore,
|
trafficDataUsageStore,
|
||||||
|
releasePlanTemplateStore,
|
||||||
|
releasePlanStore,
|
||||||
};
|
};
|
||||||
const versionServiceStores = { settingStore };
|
const versionServiceStores = { settingStore };
|
||||||
const getActiveUsers = createGetActiveUsers(db);
|
const getActiveUsers = createGetActiveUsers(db);
|
||||||
@ -160,6 +167,12 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
const featureStrategiesReadModel = new FakeFeatureStrategiesReadModel();
|
const featureStrategiesReadModel = new FakeFeatureStrategiesReadModel();
|
||||||
const trafficDataUsageStore = new FakeTrafficDataUsageStore();
|
const trafficDataUsageStore = new FakeTrafficDataUsageStore();
|
||||||
const featureStrategiesStore = new FakeFeatureStrategiesStore();
|
const featureStrategiesStore = new FakeFeatureStrategiesStore();
|
||||||
|
const releasePlanTemplateStore = {
|
||||||
|
count: () => Promise.resolve(0),
|
||||||
|
} as ReleasePlanTemplateStore;
|
||||||
|
const releasePlanStore = {
|
||||||
|
count: () => Promise.resolve(0),
|
||||||
|
} as ReleasePlanStore;
|
||||||
const instanceStatsServiceStores = {
|
const instanceStatsServiceStores = {
|
||||||
featureToggleStore,
|
featureToggleStore,
|
||||||
userStore,
|
userStore,
|
||||||
@ -178,6 +191,8 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
featureStrategiesReadModel,
|
featureStrategiesReadModel,
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
trafficDataUsageStore,
|
trafficDataUsageStore,
|
||||||
|
releasePlanTemplateStore,
|
||||||
|
releasePlanStore,
|
||||||
};
|
};
|
||||||
|
|
||||||
const versionServiceStores = { settingStore };
|
const versionServiceStores = { settingStore };
|
||||||
|
@ -31,6 +31,8 @@ import { format, minutesToMilliseconds } from 'date-fns';
|
|||||||
import memoizee from 'memoizee';
|
import memoizee from 'memoizee';
|
||||||
import type { GetLicensedUsers } from './getLicensedUsers.js';
|
import type { GetLicensedUsers } from './getLicensedUsers.js';
|
||||||
import type { IFeatureUsageInfo } from '../../services/version-service.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';
|
export type TimeRange = 'allTime' | '30d' | '7d';
|
||||||
|
|
||||||
@ -70,6 +72,8 @@ export interface InstanceStats {
|
|||||||
maxEnvironmentStrategies: number;
|
maxEnvironmentStrategies: number;
|
||||||
maxConstraints: number;
|
maxConstraints: number;
|
||||||
maxConstraintValues: number;
|
maxConstraintValues: number;
|
||||||
|
releaseTemplates?: number;
|
||||||
|
releasePlans?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
||||||
@ -126,6 +130,10 @@ export class InstanceStatsService {
|
|||||||
|
|
||||||
private trafficDataUsageStore: ITrafficDataUsageStore;
|
private trafficDataUsageStore: ITrafficDataUsageStore;
|
||||||
|
|
||||||
|
private releasePlanTemplateStore: ReleasePlanTemplateStore;
|
||||||
|
|
||||||
|
private releasePlanStore: ReleasePlanStore;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
featureToggleStore,
|
featureToggleStore,
|
||||||
@ -145,6 +153,8 @@ export class InstanceStatsService {
|
|||||||
featureStrategiesReadModel,
|
featureStrategiesReadModel,
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
trafficDataUsageStore,
|
trafficDataUsageStore,
|
||||||
|
releasePlanTemplateStore,
|
||||||
|
releasePlanStore,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
| 'featureToggleStore'
|
| 'featureToggleStore'
|
||||||
@ -164,6 +174,8 @@ export class InstanceStatsService {
|
|||||||
| 'featureStrategiesReadModel'
|
| 'featureStrategiesReadModel'
|
||||||
| 'featureStrategiesStore'
|
| 'featureStrategiesStore'
|
||||||
| 'trafficDataUsageStore'
|
| 'trafficDataUsageStore'
|
||||||
|
| 'releasePlanTemplateStore'
|
||||||
|
| 'releasePlanStore'
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
getLogger,
|
getLogger,
|
||||||
@ -203,6 +215,8 @@ export class InstanceStatsService {
|
|||||||
this.featureStrategiesReadModel = featureStrategiesReadModel;
|
this.featureStrategiesReadModel = featureStrategiesReadModel;
|
||||||
this.featureStrategiesStore = featureStrategiesStore;
|
this.featureStrategiesStore = featureStrategiesStore;
|
||||||
this.trafficDataUsageStore = trafficDataUsageStore;
|
this.trafficDataUsageStore = trafficDataUsageStore;
|
||||||
|
this.releasePlanTemplateStore = releasePlanTemplateStore;
|
||||||
|
this.releasePlanStore = releasePlanStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory = new Map<string, () => Promise<any>>();
|
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> {
|
async getStats(): Promise<InstanceStats> {
|
||||||
const versionInfo = await this.versionService.getVersionInfo();
|
const versionInfo = await this.versionService.getVersionInfo();
|
||||||
const [
|
const [
|
||||||
@ -326,6 +354,8 @@ export class InstanceStatsService {
|
|||||||
maxEnvironmentStrategies,
|
maxEnvironmentStrategies,
|
||||||
maxConstraintValues,
|
maxConstraintValues,
|
||||||
maxConstraints,
|
maxConstraints,
|
||||||
|
releaseTemplates,
|
||||||
|
releasePlans,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getArchivedToggleCount(),
|
this.getArchivedToggleCount(),
|
||||||
@ -370,6 +400,8 @@ export class InstanceStatsService {
|
|||||||
this.featureStrategiesReadModel,
|
this.featureStrategiesReadModel,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
this.getReleaseTemplates(),
|
||||||
|
this.getReleasePlans(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -408,6 +440,8 @@ export class InstanceStatsService {
|
|||||||
maxEnvironmentStrategies: maxEnvironmentStrategies?.count ?? 0,
|
maxEnvironmentStrategies: maxEnvironmentStrategies?.count ?? 0,
|
||||||
maxConstraintValues: maxConstraintValues?.count ?? 0,
|
maxConstraintValues: maxConstraintValues?.count ?? 0,
|
||||||
maxConstraints: maxConstraints?.count ?? 0,
|
maxConstraints: maxConstraints?.count ?? 0,
|
||||||
|
releaseTemplates,
|
||||||
|
releasePlans,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,6 +469,8 @@ export class InstanceStatsService {
|
|||||||
postgresVersion,
|
postgresVersion,
|
||||||
licenseType,
|
licenseType,
|
||||||
hostedBy,
|
hostedBy,
|
||||||
|
releaseTemplates,
|
||||||
|
releasePlans,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getRegisteredUsers(),
|
this.getRegisteredUsers(),
|
||||||
@ -458,6 +494,8 @@ export class InstanceStatsService {
|
|||||||
this.postgresVersion(),
|
this.postgresVersion(),
|
||||||
this.getLicenseType(),
|
this.getLicenseType(),
|
||||||
this.getHostedBy(),
|
this.getHostedBy(),
|
||||||
|
this.getReleaseTemplates(),
|
||||||
|
this.getReleasePlans(),
|
||||||
]);
|
]);
|
||||||
const versionInfo = await this.versionService.getVersionInfo();
|
const versionInfo = await this.versionService.getVersionInfo();
|
||||||
|
|
||||||
@ -493,6 +531,8 @@ export class InstanceStatsService {
|
|||||||
postgresVersion,
|
postgresVersion,
|
||||||
licenseType,
|
licenseType,
|
||||||
hostedBy,
|
hostedBy,
|
||||||
|
releaseTemplates,
|
||||||
|
releasePlans,
|
||||||
};
|
};
|
||||||
return featureInfo;
|
return featureInfo;
|
||||||
}
|
}
|
||||||
@ -663,7 +703,7 @@ export class InstanceStatsService {
|
|||||||
.reduce((a, b) => a + b, 0);
|
.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
const sum = sha256(
|
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 };
|
return { ...instanceStats, sum, projects: totalProjects };
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,18 @@ export const instanceAdminStatsSchema = {
|
|||||||
description:
|
description:
|
||||||
'The highest number of constraint values used on a single constraint.',
|
'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: {
|
sum: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description:
|
||||||
|
@ -131,6 +131,8 @@ class InstanceAdminController extends Controller {
|
|||||||
maxEnvironmentStrategies: 20,
|
maxEnvironmentStrategies: 20,
|
||||||
maxConstraints: 17,
|
maxConstraints: 17,
|
||||||
maxConstraintValues: 123,
|
maxConstraintValues: 123,
|
||||||
|
releaseTemplates: 3,
|
||||||
|
releasePlans: 5,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ const fakeTelemetryData = {
|
|||||||
postgresVersion: '17.1 (Debian 17.1-1.pgdg120+1)',
|
postgresVersion: '17.1 (Debian 17.1-1.pgdg120+1)',
|
||||||
licenseType: 'test',
|
licenseType: 'test',
|
||||||
hostedBy: 'self-hosted',
|
hostedBy: 'self-hosted',
|
||||||
|
releaseTemplates: 2,
|
||||||
|
releasePlans: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
test('yields current versions', async () => {
|
test('yields current versions', async () => {
|
||||||
|
@ -52,6 +52,8 @@ export interface IFeatureUsageInfo {
|
|||||||
postgresVersion: string;
|
postgresVersion: string;
|
||||||
licenseType: string;
|
licenseType: string;
|
||||||
hostedBy: string;
|
hostedBy: string;
|
||||||
|
releaseTemplates: number;
|
||||||
|
releasePlans: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VersionService {
|
export default class VersionService {
|
||||||
|
@ -124,7 +124,7 @@ test('should return signed instance statistics', async () => {
|
|||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.instanceId).toBe('test-static');
|
expect(res.body.instanceId).toBe('test-static');
|
||||||
expect(res.body.sum).toBe(
|
expect(res.body.sum).toBe(
|
||||||
'd9bac94bba7afa20d98f0a9d54a84b79a6668f8103b8f89db85d05d38e84f519',
|
'a5fd70e5ba5dfa02644404d4d075cb7f783487f607fbc00e8e4bc0aef41fd81a',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
12
src/test/fixtures/store.ts
vendored
12
src/test/fixtures/store.ts
vendored
@ -137,9 +137,15 @@ const createStores: () => IUnleashStores = () => {
|
|||||||
uniqueConnectionReadModel: new UniqueConnectionReadModel(
|
uniqueConnectionReadModel: new UniqueConnectionReadModel(
|
||||||
uniqueConnectionStore,
|
uniqueConnectionStore,
|
||||||
),
|
),
|
||||||
releasePlanStore: {} as ReleasePlanStore,
|
releasePlanStore: {
|
||||||
releasePlanMilestoneStore: {} as ReleasePlanMilestoneStore,
|
count: () => Promise.resolve(0),
|
||||||
releasePlanTemplateStore: {} as ReleasePlanTemplateStore,
|
} as ReleasePlanStore,
|
||||||
|
releasePlanMilestoneStore: {
|
||||||
|
count: () => Promise.resolve(0),
|
||||||
|
} as ReleasePlanMilestoneStore,
|
||||||
|
releasePlanTemplateStore: {
|
||||||
|
count: () => Promise.resolve(0),
|
||||||
|
} as ReleasePlanTemplateStore,
|
||||||
releasePlanMilestoneStrategyStore:
|
releasePlanMilestoneStrategyStore:
|
||||||
{} as ReleasePlanMilestoneStrategyStore,
|
{} as ReleasePlanMilestoneStrategyStore,
|
||||||
featureLinkStore: new FakeFeatureLinkStore(),
|
featureLinkStore: new FakeFeatureLinkStore(),
|
||||||
|
Loading…
Reference in New Issue
Block a user