mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
chore: add edge instances to instance stats (#10839)
https://linear.app/unleash/issue/2-3979/add-edge-instances-to-self-reported-instance-stats Adds edge instances to instance stats.
This commit is contained in:
parent
09fcbe3d49
commit
8ba35507cd
@ -964,6 +964,7 @@ export * from './instanceAdminStatsSchemaActiveUsers.js';
|
|||||||
export * from './instanceAdminStatsSchemaApiTokens.js';
|
export * from './instanceAdminStatsSchemaApiTokens.js';
|
||||||
export * from './instanceAdminStatsSchemaClientAppsItem.js';
|
export * from './instanceAdminStatsSchemaClientAppsItem.js';
|
||||||
export * from './instanceAdminStatsSchemaClientAppsItemRange.js';
|
export * from './instanceAdminStatsSchemaClientAppsItemRange.js';
|
||||||
|
export * from './instanceAdminStatsSchemaEdgeInstances.js';
|
||||||
export * from './instanceAdminStatsSchemaPreviousDayMetricsBucketsCount.js';
|
export * from './instanceAdminStatsSchemaPreviousDayMetricsBucketsCount.js';
|
||||||
export * from './instanceAdminStatsSchemaProductionChanges.js';
|
export * from './instanceAdminStatsSchemaProductionChanges.js';
|
||||||
export * from './instanceInsightsSchema.js';
|
export * from './instanceInsightsSchema.js';
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
import type { InstanceAdminStatsSchemaActiveUsers } from './instanceAdminStatsSchemaActiveUsers.js';
|
import type { InstanceAdminStatsSchemaActiveUsers } from './instanceAdminStatsSchemaActiveUsers.js';
|
||||||
import type { InstanceAdminStatsSchemaApiTokens } from './instanceAdminStatsSchemaApiTokens.js';
|
import type { InstanceAdminStatsSchemaApiTokens } from './instanceAdminStatsSchemaApiTokens.js';
|
||||||
import type { InstanceAdminStatsSchemaClientAppsItem } from './instanceAdminStatsSchemaClientAppsItem.js';
|
import type { InstanceAdminStatsSchemaClientAppsItem } from './instanceAdminStatsSchemaClientAppsItem.js';
|
||||||
|
import type { InstanceAdminStatsSchemaEdgeInstances } from './instanceAdminStatsSchemaEdgeInstances.js';
|
||||||
import type { InstanceAdminStatsSchemaPreviousDayMetricsBucketsCount } from './instanceAdminStatsSchemaPreviousDayMetricsBucketsCount.js';
|
import type { InstanceAdminStatsSchemaPreviousDayMetricsBucketsCount } from './instanceAdminStatsSchemaPreviousDayMetricsBucketsCount.js';
|
||||||
import type { InstanceAdminStatsSchemaProductionChanges } from './instanceAdminStatsSchemaProductionChanges.js';
|
import type { InstanceAdminStatsSchemaProductionChanges } from './instanceAdminStatsSchemaProductionChanges.js';
|
||||||
|
|
||||||
@ -24,6 +25,8 @@ export interface InstanceAdminStatsSchema {
|
|||||||
* @minimum 0
|
* @minimum 0
|
||||||
*/
|
*/
|
||||||
contextFields?: number;
|
contextFields?: number;
|
||||||
|
/** The rounded up average number of edge instances in the last month and month before last */
|
||||||
|
edgeInstances?: InstanceAdminStatsSchemaEdgeInstances;
|
||||||
/**
|
/**
|
||||||
* The number of environments defined in this instance
|
* The number of environments defined in this instance
|
||||||
* @minimum 0
|
* @minimum 0
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Generated by Orval
|
||||||
|
* Do not edit manually.
|
||||||
|
* See `gen:api` script in package.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rounded up average number of edge instances in the last month and month before last
|
||||||
|
*/
|
||||||
|
export type InstanceAdminStatsSchemaEdgeInstances = {
|
||||||
|
/**
|
||||||
|
* The rounded up average number of edge instances in the last month
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
lastMonth?: number;
|
||||||
|
/**
|
||||||
|
* The rounded up average number of edge instances in the month before last
|
||||||
|
* @minimum 0
|
||||||
|
*/
|
||||||
|
monthBeforeLast?: number;
|
||||||
|
};
|
||||||
@ -50,6 +50,10 @@ import {
|
|||||||
} from './getLicensedUsers.js';
|
} from './getLicensedUsers.js';
|
||||||
import { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
import { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
||||||
import { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
import { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
||||||
|
import {
|
||||||
|
createFakeGetEdgeInstances,
|
||||||
|
createGetEdgeInstances,
|
||||||
|
} from './getEdgeInstances.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;
|
||||||
@ -134,6 +138,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
const getActiveUsers = createGetActiveUsers(db);
|
const getActiveUsers = createGetActiveUsers(db);
|
||||||
const getProductionChanges = createGetProductionChanges(db);
|
const getProductionChanges = createGetProductionChanges(db);
|
||||||
const getLicencedUsers = createGetLicensedUsers(db);
|
const getLicencedUsers = createGetLicensedUsers(db);
|
||||||
|
const getEdgeInstances = createGetEdgeInstances(db);
|
||||||
const versionService = new VersionService(versionServiceStores, config);
|
const versionService = new VersionService(versionServiceStores, config);
|
||||||
|
|
||||||
const instanceStatsService = new InstanceStatsService(
|
const instanceStatsService = new InstanceStatsService(
|
||||||
@ -143,6 +148,7 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
getLicencedUsers,
|
getLicencedUsers,
|
||||||
|
getEdgeInstances,
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
@ -199,6 +205,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
const getActiveUsers = createFakeGetActiveUsers();
|
const getActiveUsers = createFakeGetActiveUsers();
|
||||||
const getLicensedUsers = createFakeGetLicensedUsers();
|
const getLicensedUsers = createFakeGetLicensedUsers();
|
||||||
const getProductionChanges = createFakeGetProductionChanges();
|
const getProductionChanges = createFakeGetProductionChanges();
|
||||||
|
const getEdgeInstances = createFakeGetEdgeInstances();
|
||||||
const versionService = new VersionService(versionServiceStores, config);
|
const versionService = new VersionService(versionServiceStores, config);
|
||||||
|
|
||||||
const instanceStatsService = new InstanceStatsService(
|
const instanceStatsService = new InstanceStatsService(
|
||||||
@ -208,6 +215,7 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
getLicensedUsers,
|
getLicensedUsers,
|
||||||
|
getEdgeInstances,
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
|
|||||||
132
src/lib/features/instance-stats/getEdgeInstances.e2e.test.ts
Normal file
132
src/lib/features/instance-stats/getEdgeInstances.e2e.test.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import {
|
||||||
|
createGetEdgeInstances,
|
||||||
|
type GetEdgeInstances,
|
||||||
|
} from './getEdgeInstances.js';
|
||||||
|
import dbInit, {
|
||||||
|
type ITestDb,
|
||||||
|
} from '../../../test/e2e/helpers/database-init.js';
|
||||||
|
import getLogger from '../../../test/fixtures/no-logger.js';
|
||||||
|
|
||||||
|
let db: ITestDb;
|
||||||
|
let getEdgeInstances: GetEdgeInstances;
|
||||||
|
|
||||||
|
const TABLE = 'edge_node_presence';
|
||||||
|
|
||||||
|
const firstDayOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1);
|
||||||
|
const addMonths = (d: Date, n: number) =>
|
||||||
|
new Date(d.getFullYear(), d.getMonth() + n, 1);
|
||||||
|
|
||||||
|
const monthWindows = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const thisMonthStart = firstDayOfMonth(now);
|
||||||
|
const lastMonthStart = addMonths(thisMonthStart, -1);
|
||||||
|
const monthBeforeLastStart = addMonths(thisMonthStart, -2);
|
||||||
|
const lastMonthEnd = thisMonthStart;
|
||||||
|
const monthBeforeLastEnd = lastMonthStart;
|
||||||
|
return {
|
||||||
|
monthBeforeLastStart,
|
||||||
|
monthBeforeLastEnd,
|
||||||
|
lastMonthStart,
|
||||||
|
lastMonthEnd,
|
||||||
|
thisMonthStart,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const atMidMonth = (start: Date) =>
|
||||||
|
new Date(start.getFullYear(), start.getMonth(), 15);
|
||||||
|
const atLateMonth = (start: Date) =>
|
||||||
|
new Date(start.getFullYear(), start.getMonth(), 25);
|
||||||
|
|
||||||
|
const rowsForBucket = (count: number, when: Date) =>
|
||||||
|
Array.from({ length: count }, (_, i) => ({
|
||||||
|
bucket_ts: when,
|
||||||
|
node_ephem_id: `node-${when.getTime()}-${i}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
db = await dbInit('edge_instances_e2e', getLogger);
|
||||||
|
getEdgeInstances = createGetEdgeInstances(db.rawDatabase);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.rawDatabase(TABLE).delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns 0 for both months when no data', async () => {
|
||||||
|
await expect(getEdgeInstances()).resolves.toEqual({
|
||||||
|
lastMonth: 0,
|
||||||
|
monthBeforeLast: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts only last full month', async () => {
|
||||||
|
const { lastMonthStart } = monthWindows();
|
||||||
|
const mid = atMidMonth(lastMonthStart);
|
||||||
|
const late = atLateMonth(lastMonthStart);
|
||||||
|
await db
|
||||||
|
.rawDatabase(TABLE)
|
||||||
|
.insert([...rowsForBucket(3, mid), ...rowsForBucket(7, late)]);
|
||||||
|
await expect(getEdgeInstances()).resolves.toEqual({
|
||||||
|
lastMonth: 5,
|
||||||
|
monthBeforeLast: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('counts only month before last', async () => {
|
||||||
|
const { monthBeforeLastStart } = monthWindows();
|
||||||
|
const mid = atMidMonth(monthBeforeLastStart);
|
||||||
|
const late = atLateMonth(monthBeforeLastStart);
|
||||||
|
await db
|
||||||
|
.rawDatabase(TABLE)
|
||||||
|
.insert([...rowsForBucket(2, mid), ...rowsForBucket(5, late)]);
|
||||||
|
await expect(getEdgeInstances()).resolves.toEqual({
|
||||||
|
lastMonth: 0,
|
||||||
|
monthBeforeLast: 4,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('separates months correctly when both have data', async () => {
|
||||||
|
const { monthBeforeLastStart, lastMonthStart } = monthWindows();
|
||||||
|
const pMid = atMidMonth(monthBeforeLastStart);
|
||||||
|
const pLate = atLateMonth(monthBeforeLastStart);
|
||||||
|
const lMid = atMidMonth(lastMonthStart);
|
||||||
|
const lLate = atLateMonth(lastMonthStart);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.rawDatabase(TABLE)
|
||||||
|
.insert([
|
||||||
|
...rowsForBucket(4, pMid),
|
||||||
|
...rowsForBucket(6, pLate),
|
||||||
|
...rowsForBucket(3, lMid),
|
||||||
|
...rowsForBucket(7, lLate),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(getEdgeInstances()).resolves.toEqual({
|
||||||
|
lastMonth: 5,
|
||||||
|
monthBeforeLast: 5,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ignores current month data', async () => {
|
||||||
|
const { thisMonthStart, lastMonthStart } = monthWindows();
|
||||||
|
const lMid = atMidMonth(lastMonthStart);
|
||||||
|
const lLate = atLateMonth(lastMonthStart);
|
||||||
|
const tMid = atMidMonth(thisMonthStart);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.rawDatabase(TABLE)
|
||||||
|
.insert([
|
||||||
|
...rowsForBucket(10, tMid),
|
||||||
|
...rowsForBucket(2, lMid),
|
||||||
|
...rowsForBucket(4, lLate),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(getEdgeInstances()).resolves.toEqual({
|
||||||
|
lastMonth: 3,
|
||||||
|
monthBeforeLast: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
69
src/lib/features/instance-stats/getEdgeInstances.ts
Normal file
69
src/lib/features/instance-stats/getEdgeInstances.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import type { Db } from '../../types/index.js';
|
||||||
|
|
||||||
|
const TABLE = 'edge_node_presence';
|
||||||
|
|
||||||
|
export type GetEdgeInstances = () => Promise<{
|
||||||
|
lastMonth: number;
|
||||||
|
monthBeforeLast: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const createGetEdgeInstances =
|
||||||
|
(db: Db): GetEdgeInstances =>
|
||||||
|
async () => {
|
||||||
|
const result = await db
|
||||||
|
.with('buckets', (qb) =>
|
||||||
|
qb
|
||||||
|
.from(TABLE)
|
||||||
|
.whereRaw(
|
||||||
|
"bucket_ts >= date_trunc('month', NOW()) - INTERVAL '2 months'",
|
||||||
|
)
|
||||||
|
.groupBy('bucket_ts')
|
||||||
|
.select(
|
||||||
|
db.raw('bucket_ts'),
|
||||||
|
db.raw('COUNT(*)::int AS active_nodes'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.from('buckets')
|
||||||
|
.select({
|
||||||
|
lastMonth: db.raw(`
|
||||||
|
COALESCE(
|
||||||
|
CEIL(
|
||||||
|
AVG(active_nodes)
|
||||||
|
FILTER (
|
||||||
|
WHERE bucket_ts >= date_trunc('month', NOW()) - INTERVAL '1 month'
|
||||||
|
AND bucket_ts < date_trunc('month', NOW())
|
||||||
|
)
|
||||||
|
)::int,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
`),
|
||||||
|
monthBeforeLast: db.raw(`
|
||||||
|
COALESCE(
|
||||||
|
CEIL(
|
||||||
|
AVG(active_nodes)
|
||||||
|
FILTER (
|
||||||
|
WHERE bucket_ts >= date_trunc('month', NOW()) - INTERVAL '2 months'
|
||||||
|
AND bucket_ts < date_trunc('month', NOW()) - INTERVAL '1 month'
|
||||||
|
)
|
||||||
|
)::int,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
`),
|
||||||
|
})
|
||||||
|
.first();
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastMonth: Number(result?.lastMonth ?? 0),
|
||||||
|
monthBeforeLast: Number(result?.monthBeforeLast ?? 0),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFakeGetEdgeInstances =
|
||||||
|
(
|
||||||
|
edgeInstances: Awaited<ReturnType<GetEdgeInstances>> = {
|
||||||
|
lastMonth: 0,
|
||||||
|
monthBeforeLast: 0,
|
||||||
|
},
|
||||||
|
): GetEdgeInstances =>
|
||||||
|
() =>
|
||||||
|
Promise.resolve(edgeInstances);
|
||||||
@ -14,6 +14,7 @@ import type {
|
|||||||
import { createFakeGetLicensedUsers } from './getLicensedUsers.js';
|
import { createFakeGetLicensedUsers } from './getLicensedUsers.js';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { DEFAULT_ENV } from '../../server-impl.js';
|
import { DEFAULT_ENV } from '../../server-impl.js';
|
||||||
|
import { createFakeGetEdgeInstances } from './getEdgeInstances.js';
|
||||||
|
|
||||||
let instanceStatsService: InstanceStatsService;
|
let instanceStatsService: InstanceStatsService;
|
||||||
let versionService: VersionService;
|
let versionService: VersionService;
|
||||||
@ -39,6 +40,7 @@ beforeEach(() => {
|
|||||||
createFakeGetActiveUsers(),
|
createFakeGetActiveUsers(),
|
||||||
createFakeGetProductionChanges(),
|
createFakeGetProductionChanges(),
|
||||||
createFakeGetLicensedUsers(),
|
createFakeGetLicensedUsers(),
|
||||||
|
createFakeGetEdgeInstances(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { collectAggDbMetrics } = registerPrometheusMetrics(
|
const { collectAggDbMetrics } = registerPrometheusMetrics(
|
||||||
|
|||||||
@ -33,6 +33,7 @@ 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 { ReleasePlanTemplateStore } from '../release-plans/release-plan-template-store.js';
|
||||||
import type { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
import type { ReleasePlanStore } from '../release-plans/release-plan-store.js';
|
||||||
|
import type { GetEdgeInstances } from './getEdgeInstances.js';
|
||||||
|
|
||||||
export type TimeRange = 'allTime' | '30d' | '7d';
|
export type TimeRange = 'allTime' | '30d' | '7d';
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ export interface InstanceStats {
|
|||||||
maxConstraintValues: number;
|
maxConstraintValues: number;
|
||||||
releaseTemplates?: number;
|
releaseTemplates?: number;
|
||||||
releasePlans?: number;
|
releasePlans?: number;
|
||||||
|
edgeInstances?: Awaited<ReturnType<GetEdgeInstances>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
||||||
@ -124,6 +126,8 @@ export class InstanceStatsService {
|
|||||||
|
|
||||||
getProductionChanges: GetProductionChanges;
|
getProductionChanges: GetProductionChanges;
|
||||||
|
|
||||||
|
getEdgeInstances: GetEdgeInstances;
|
||||||
|
|
||||||
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
|
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
|
||||||
|
|
||||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||||
@ -185,6 +189,7 @@ export class InstanceStatsService {
|
|||||||
getActiveUsers: GetActiveUsers,
|
getActiveUsers: GetActiveUsers,
|
||||||
getProductionChanges: GetProductionChanges,
|
getProductionChanges: GetProductionChanges,
|
||||||
getLicencedUsers: GetLicensedUsers,
|
getLicencedUsers: GetLicensedUsers,
|
||||||
|
getEdgeInstances: GetEdgeInstances,
|
||||||
) {
|
) {
|
||||||
this.strategyStore = strategyStore;
|
this.strategyStore = strategyStore;
|
||||||
this.userStore = userStore;
|
this.userStore = userStore;
|
||||||
@ -209,6 +214,8 @@ export class InstanceStatsService {
|
|||||||
'getProductionChanges',
|
'getProductionChanges',
|
||||||
getProductionChanges.bind(this),
|
getProductionChanges.bind(this),
|
||||||
);
|
);
|
||||||
|
this.getEdgeInstances = () =>
|
||||||
|
this.memorize('getEdgeInstances', getEdgeInstances.bind(this));
|
||||||
this.apiTokenStore = apiTokenStore;
|
this.apiTokenStore = apiTokenStore;
|
||||||
this.clientMetricsStore = clientMetricsStoreV2;
|
this.clientMetricsStore = clientMetricsStoreV2;
|
||||||
this.flagResolver = flagResolver;
|
this.flagResolver = flagResolver;
|
||||||
@ -356,6 +363,7 @@ export class InstanceStatsService {
|
|||||||
maxConstraints,
|
maxConstraints,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getArchivedToggleCount(),
|
this.getArchivedToggleCount(),
|
||||||
@ -402,6 +410,7 @@ export class InstanceStatsService {
|
|||||||
),
|
),
|
||||||
this.getReleaseTemplates(),
|
this.getReleaseTemplates(),
|
||||||
this.getReleasePlans(),
|
this.getReleasePlans(),
|
||||||
|
this.getEdgeInstances(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -442,6 +451,7 @@ export class InstanceStatsService {
|
|||||||
maxConstraints: maxConstraints?.count ?? 0,
|
maxConstraints: maxConstraints?.count ?? 0,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,6 +481,7 @@ export class InstanceStatsService {
|
|||||||
hostedBy,
|
hostedBy,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstances,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getRegisteredUsers(),
|
this.getRegisteredUsers(),
|
||||||
@ -496,6 +507,7 @@ export class InstanceStatsService {
|
|||||||
this.getHostedBy(),
|
this.getHostedBy(),
|
||||||
this.getReleaseTemplates(),
|
this.getReleaseTemplates(),
|
||||||
this.getReleasePlans(),
|
this.getReleasePlans(),
|
||||||
|
this.getEdgeInstances(),
|
||||||
]);
|
]);
|
||||||
const versionInfo = await this.versionService.getVersionInfo();
|
const versionInfo = await this.versionService.getVersionInfo();
|
||||||
|
|
||||||
@ -533,6 +545,8 @@ export class InstanceStatsService {
|
|||||||
hostedBy,
|
hostedBy,
|
||||||
releaseTemplates,
|
releaseTemplates,
|
||||||
releasePlans,
|
releasePlans,
|
||||||
|
edgeInstancesLastMonth: edgeInstances.lastMonth,
|
||||||
|
edgeInstancesMonthBeforeLast: edgeInstances.monthBeforeLast,
|
||||||
};
|
};
|
||||||
return featureInfo;
|
return featureInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ import dbInit, { type ITestDb } from '../test/e2e/helpers/database-init.js';
|
|||||||
import { FeatureLifecycleStore } from './features/feature-lifecycle/feature-lifecycle-store.js';
|
import { FeatureLifecycleStore } from './features/feature-lifecycle/feature-lifecycle-store.js';
|
||||||
import { FeatureLifecycleReadModel } from './features/feature-lifecycle/feature-lifecycle-read-model.js';
|
import { FeatureLifecycleReadModel } from './features/feature-lifecycle/feature-lifecycle-read-model.js';
|
||||||
import { createFakeGetLicensedUsers } from './features/instance-stats/getLicensedUsers.js';
|
import { createFakeGetLicensedUsers } from './features/instance-stats/getLicensedUsers.js';
|
||||||
|
import { createFakeGetEdgeInstances } from './features/instance-stats/getEdgeInstances.js';
|
||||||
|
|
||||||
const monitor = createMetricsMonitor();
|
const monitor = createMetricsMonitor();
|
||||||
const eventBus = new EventEmitter();
|
const eventBus = new EventEmitter();
|
||||||
@ -79,6 +80,7 @@ beforeAll(async () => {
|
|||||||
createFakeGetActiveUsers(),
|
createFakeGetActiveUsers(),
|
||||||
createFakeGetProductionChanges(),
|
createFakeGetProductionChanges(),
|
||||||
createFakeGetLicensedUsers(),
|
createFakeGetLicensedUsers(),
|
||||||
|
createFakeGetEdgeInstances(),
|
||||||
);
|
);
|
||||||
|
|
||||||
schedulerService = new SchedulerService(
|
schedulerService = new SchedulerService(
|
||||||
|
|||||||
@ -284,6 +284,27 @@ export const instanceAdminStatsSchema = {
|
|||||||
example: 1,
|
example: 1,
|
||||||
description: 'The number of release plans in this instance',
|
description: 'The number of release plans in this instance',
|
||||||
},
|
},
|
||||||
|
edgeInstances: {
|
||||||
|
type: 'object',
|
||||||
|
description:
|
||||||
|
'The rounded up average number of edge instances in the last month and month before last',
|
||||||
|
properties: {
|
||||||
|
lastMonth: {
|
||||||
|
type: 'integer',
|
||||||
|
description:
|
||||||
|
'The rounded up average number of edge instances in the last month',
|
||||||
|
example: 10,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
monthBeforeLast: {
|
||||||
|
type: 'integer',
|
||||||
|
description:
|
||||||
|
'The rounded up average number of edge instances in the month before last',
|
||||||
|
example: 12,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
sum: {
|
sum: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -133,6 +133,10 @@ class InstanceAdminController extends Controller {
|
|||||||
maxConstraintValues: 123,
|
maxConstraintValues: 123,
|
||||||
releaseTemplates: 3,
|
releaseTemplates: 3,
|
||||||
releasePlans: 5,
|
releasePlans: 5,
|
||||||
|
edgeInstances: {
|
||||||
|
lastMonth: 10,
|
||||||
|
monthBeforeLast: 15,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,8 @@ const fakeTelemetryData = {
|
|||||||
hostedBy: 'self-hosted',
|
hostedBy: 'self-hosted',
|
||||||
releaseTemplates: 2,
|
releaseTemplates: 2,
|
||||||
releasePlans: 4,
|
releasePlans: 4,
|
||||||
|
edgeInstancesLastMonth: 0,
|
||||||
|
edgeInstancesMonthBeforeLast: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
test('yields current versions', async () => {
|
test('yields current versions', async () => {
|
||||||
|
|||||||
@ -54,6 +54,8 @@ export interface IFeatureUsageInfo {
|
|||||||
hostedBy: string;
|
hostedBy: string;
|
||||||
releaseTemplates: number;
|
releaseTemplates: number;
|
||||||
releasePlans: number;
|
releasePlans: number;
|
||||||
|
edgeInstancesLastMonth?: number;
|
||||||
|
edgeInstancesMonthBeforeLast?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VersionService {
|
export default class VersionService {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user