mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-12 13:48:35 +02:00
refactor: lifecycle stage duration outside instance stats (#7442)
This commit is contained in:
parent
6a9a2c687d
commit
c3fa468a9d
@ -3,11 +3,17 @@ import type {
|
|||||||
StageCount,
|
StageCount,
|
||||||
StageCountByProject,
|
StageCountByProject,
|
||||||
} from './feature-lifecycle-read-model-type';
|
} from './feature-lifecycle-read-model-type';
|
||||||
import type { IFeatureLifecycleStage } from '../../types';
|
import type {
|
||||||
|
IFeatureLifecycleStage,
|
||||||
|
IProjectLifecycleStageDuration,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
export class FakeFeatureLifecycleReadModel
|
export class FakeFeatureLifecycleReadModel
|
||||||
implements IFeatureLifecycleReadModel
|
implements IFeatureLifecycleReadModel
|
||||||
{
|
{
|
||||||
|
getAllWithStageDuration(): Promise<IProjectLifecycleStageDuration[]> {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
getStageCount(): Promise<StageCount[]> {
|
getStageCount(): Promise<StageCount[]> {
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import type {
|
|||||||
FeatureLifecycleStage,
|
FeatureLifecycleStage,
|
||||||
IFeatureLifecycleStore,
|
IFeatureLifecycleStore,
|
||||||
FeatureLifecycleView,
|
FeatureLifecycleView,
|
||||||
FeatureLifecycleProjectItem,
|
|
||||||
} from './feature-lifecycle-store-type';
|
} from './feature-lifecycle-store-type';
|
||||||
|
|
||||||
export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore {
|
export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore {
|
||||||
@ -41,18 +40,6 @@ export class FakeFeatureLifecycleStore implements IFeatureLifecycleStore {
|
|||||||
return this.lifecycles[feature] || [];
|
return this.lifecycles[feature] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<FeatureLifecycleProjectItem[]> {
|
|
||||||
const result = Object.entries(this.lifecycles).flatMap(
|
|
||||||
([key, items]): FeatureLifecycleProjectItem[] =>
|
|
||||||
items.map((item) => ({
|
|
||||||
...item,
|
|
||||||
feature: key,
|
|
||||||
project: 'fake-project',
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async delete(feature: string): Promise<void> {
|
async delete(feature: string): Promise<void> {
|
||||||
this.lifecycles[feature] = [];
|
this.lifecycles[feature] = [];
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import type { IFeatureLifecycleStage, StageName } from '../../types';
|
import type {
|
||||||
|
IFeatureLifecycleStage,
|
||||||
|
IProjectLifecycleStageDuration,
|
||||||
|
StageName,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
export type StageCount = {
|
export type StageCount = {
|
||||||
stage: StageName;
|
stage: StageName;
|
||||||
@ -15,4 +19,5 @@ export interface IFeatureLifecycleReadModel {
|
|||||||
): Promise<IFeatureLifecycleStage | undefined>;
|
): Promise<IFeatureLifecycleStage | undefined>;
|
||||||
getStageCount(): Promise<StageCount[]>;
|
getStageCount(): Promise<StageCount[]>;
|
||||||
getStageCountByProject(): Promise<StageCountByProject[]>;
|
getStageCountByProject(): Promise<StageCountByProject[]>;
|
||||||
|
getAllWithStageDuration(): Promise<IProjectLifecycleStageDuration[]>;
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,11 @@ import { getCurrentStage } from './get-current-stage';
|
|||||||
import type {
|
import type {
|
||||||
IFeatureLifecycleStage,
|
IFeatureLifecycleStage,
|
||||||
IFlagResolver,
|
IFlagResolver,
|
||||||
|
IProjectLifecycleStageDuration,
|
||||||
StageName,
|
StageName,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
import { calculateStageDurations } from './calculate-stage-durations';
|
||||||
|
import type { FeatureLifecycleProjectItem } from './feature-lifecycle-store-type';
|
||||||
|
|
||||||
type DBType = {
|
type DBType = {
|
||||||
feature: string;
|
feature: string;
|
||||||
@ -18,6 +21,10 @@ type DBType = {
|
|||||||
created_at: Date;
|
created_at: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DBProjectType = DBType & {
|
||||||
|
project: string;
|
||||||
|
};
|
||||||
|
|
||||||
export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
||||||
private db: Db;
|
private db: Db;
|
||||||
|
|
||||||
@ -106,4 +113,31 @@ export class FeatureLifecycleReadModel implements IFeatureLifecycleReadModel {
|
|||||||
|
|
||||||
return getCurrentStage(stages);
|
return getCurrentStage(stages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getAll(): Promise<FeatureLifecycleProjectItem[]> {
|
||||||
|
const results = await this.db('feature_lifecycles as flc')
|
||||||
|
.select('flc.feature', 'flc.stage', 'flc.created_at', 'f.project')
|
||||||
|
.leftJoin('features as f', 'f.name', 'flc.feature')
|
||||||
|
.orderBy('created_at', 'asc');
|
||||||
|
|
||||||
|
return results.map(
|
||||||
|
({ feature, stage, created_at, project }: DBProjectType) => ({
|
||||||
|
feature,
|
||||||
|
stage,
|
||||||
|
project,
|
||||||
|
enteredStageAt: new Date(created_at),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAllWithStageDuration(): Promise<
|
||||||
|
IProjectLifecycleStageDuration[]
|
||||||
|
> {
|
||||||
|
if (!this.flagResolver.isEnabled('featureLifecycleMetrics')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const featureLifeCycles = await this.getAll();
|
||||||
|
return calculateStageDurations(featureLifeCycles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
type IEventStore,
|
type IEventStore,
|
||||||
type IFeatureEnvironmentStore,
|
type IFeatureEnvironmentStore,
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
type IProjectLifecycleStageDuration,
|
|
||||||
type IUnleashConfig,
|
type IUnleashConfig,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import type {
|
import type {
|
||||||
@ -21,7 +20,6 @@ import EventEmitter from 'events';
|
|||||||
import type { Logger } from '../../logger';
|
import type { Logger } from '../../logger';
|
||||||
import type EventService from '../events/event-service';
|
import type EventService from '../events/event-service';
|
||||||
import type { FeatureLifecycleCompletedSchema } from '../../openapi';
|
import type { FeatureLifecycleCompletedSchema } from '../../openapi';
|
||||||
import { calculateStageDurations } from './calculate-stage-durations';
|
|
||||||
import type { IClientMetricsEnv } from '../metrics/client-metrics/client-metrics-store-v2-type';
|
import type { IClientMetricsEnv } from '../metrics/client-metrics/client-metrics-store-v2-type';
|
||||||
import groupBy from 'lodash.groupby';
|
import groupBy from 'lodash.groupby';
|
||||||
|
|
||||||
@ -231,11 +229,4 @@ export class FeatureLifecycleService extends EventEmitter {
|
|||||||
await this.featureLifecycleStore.delete(feature);
|
await this.featureLifecycleStore.delete(feature);
|
||||||
await this.featureInitialized(feature);
|
await this.featureInitialized(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllWithStageDuration(): Promise<
|
|
||||||
IProjectLifecycleStageDuration[]
|
|
||||||
> {
|
|
||||||
const featureLifeCycles = await this.featureLifecycleStore.getAll();
|
|
||||||
return calculateStageDurations(featureLifeCycles);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ export type FeatureLifecycleProjectItem = FeatureLifecycleStage & {
|
|||||||
export interface IFeatureLifecycleStore {
|
export interface IFeatureLifecycleStore {
|
||||||
insert(featureLifecycleStages: FeatureLifecycleStage[]): Promise<void>;
|
insert(featureLifecycleStages: FeatureLifecycleStage[]): Promise<void>;
|
||||||
get(feature: string): Promise<FeatureLifecycleView>;
|
get(feature: string): Promise<FeatureLifecycleView>;
|
||||||
getAll(): Promise<FeatureLifecycleProjectItem[]>;
|
|
||||||
stageExists(stage: FeatureLifecycleStage): Promise<boolean>;
|
stageExists(stage: FeatureLifecycleStage): Promise<boolean>;
|
||||||
delete(feature: string): Promise<void>;
|
delete(feature: string): Promise<void>;
|
||||||
deleteAll(): Promise<void>;
|
deleteAll(): Promise<void>;
|
||||||
|
@ -40,10 +40,6 @@ import FakeSettingStore from '../../../test/fixtures/fake-setting-store';
|
|||||||
import FakeSegmentStore from '../../../test/fixtures/fake-segment-store';
|
import FakeSegmentStore from '../../../test/fixtures/fake-segment-store';
|
||||||
import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
|
import FakeStrategiesStore from '../../../test/fixtures/fake-strategies-store';
|
||||||
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
|
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
|
||||||
import {
|
|
||||||
createFakeFeatureLifecycleService,
|
|
||||||
createFeatureLifecycleService,
|
|
||||||
} from '../feature-lifecycle/createFeatureLifecycle';
|
|
||||||
|
|
||||||
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
||||||
const { eventBus, getLogger, flagResolver } = config;
|
const { eventBus, getLogger, flagResolver } = config;
|
||||||
@ -88,10 +84,6 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
getLogger,
|
getLogger,
|
||||||
flagResolver,
|
flagResolver,
|
||||||
);
|
);
|
||||||
const { featureLifecycleService } = createFeatureLifecycleService(
|
|
||||||
db,
|
|
||||||
config,
|
|
||||||
);
|
|
||||||
const instanceStatsServiceStores = {
|
const instanceStatsServiceStores = {
|
||||||
featureToggleStore,
|
featureToggleStore,
|
||||||
userStore,
|
userStore,
|
||||||
@ -133,7 +125,6 @@ export const createInstanceStatsService = (db: Db, config: IUnleashConfig) => {
|
|||||||
versionService,
|
versionService,
|
||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
featureLifecycleService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
@ -156,8 +147,6 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
const apiTokenStore = new FakeApiTokenStore();
|
const apiTokenStore = new FakeApiTokenStore();
|
||||||
const clientMetricsStoreV2 = new FakeClientMetricsStoreV2();
|
const clientMetricsStoreV2 = new FakeClientMetricsStoreV2();
|
||||||
|
|
||||||
const { featureLifecycleService } =
|
|
||||||
createFakeFeatureLifecycleService(config);
|
|
||||||
const instanceStatsServiceStores = {
|
const instanceStatsServiceStores = {
|
||||||
featureToggleStore,
|
featureToggleStore,
|
||||||
userStore,
|
userStore,
|
||||||
@ -194,7 +183,6 @@ export const createFakeInstanceStatsService = (config: IUnleashConfig) => {
|
|||||||
versionService,
|
versionService,
|
||||||
getActiveUsers,
|
getActiveUsers,
|
||||||
getProductionChanges,
|
getProductionChanges,
|
||||||
featureLifecycleService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return instanceStatsService;
|
return instanceStatsService;
|
||||||
|
@ -4,7 +4,6 @@ import createStores from '../../../test/fixtures/store';
|
|||||||
import VersionService from '../../services/version-service';
|
import VersionService from '../../services/version-service';
|
||||||
import { createFakeGetActiveUsers } from './getActiveUsers';
|
import { createFakeGetActiveUsers } from './getActiveUsers';
|
||||||
import { createFakeGetProductionChanges } from './getProductionChanges';
|
import { createFakeGetProductionChanges } from './getProductionChanges';
|
||||||
import { createFakeFeatureLifecycleService } from '../feature-lifecycle/createFeatureLifecycle';
|
|
||||||
|
|
||||||
let instanceStatsService: InstanceStatsService;
|
let instanceStatsService: InstanceStatsService;
|
||||||
let versionService: VersionService;
|
let versionService: VersionService;
|
||||||
@ -24,7 +23,6 @@ beforeEach(() => {
|
|||||||
versionService,
|
versionService,
|
||||||
createFakeGetActiveUsers(),
|
createFakeGetActiveUsers(),
|
||||||
createFakeGetProductionChanges(),
|
createFakeGetProductionChanges(),
|
||||||
createFakeFeatureLifecycleService(config).featureLifecycleService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.spyOn(instanceStatsService, 'refreshAppCountSnapshot');
|
jest.spyOn(instanceStatsService, 'refreshAppCountSnapshot');
|
||||||
|
@ -22,14 +22,12 @@ import {
|
|||||||
FEATURES_EXPORTED,
|
FEATURES_EXPORTED,
|
||||||
FEATURES_IMPORTED,
|
FEATURES_IMPORTED,
|
||||||
type IApiTokenStore,
|
type IApiTokenStore,
|
||||||
type IProjectLifecycleStageDuration,
|
|
||||||
type IFlagResolver,
|
type IFlagResolver,
|
||||||
} from '../../types';
|
} 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 type { ProjectModeCount } from '../project/project-store';
|
import type { ProjectModeCount } from '../project/project-store';
|
||||||
import type { GetProductionChanges } from './getProductionChanges';
|
import type { GetProductionChanges } from './getProductionChanges';
|
||||||
import type { FeatureLifecycleService } from '../feature-lifecycle/feature-lifecycle-service';
|
|
||||||
|
|
||||||
export type TimeRange = 'allTime' | '30d' | '7d';
|
export type TimeRange = 'allTime' | '30d' | '7d';
|
||||||
|
|
||||||
@ -63,7 +61,6 @@ export interface InstanceStats {
|
|||||||
enabledCount: number;
|
enabledCount: number;
|
||||||
variantCount: number;
|
variantCount: number;
|
||||||
};
|
};
|
||||||
featureLifeCycles: IProjectLifecycleStageDuration[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
export type InstanceStatsSigned = Omit<InstanceStats, 'projects'> & {
|
||||||
@ -94,8 +91,6 @@ export class InstanceStatsService {
|
|||||||
|
|
||||||
private eventStore: IEventStore;
|
private eventStore: IEventStore;
|
||||||
|
|
||||||
private featureLifecycleService: FeatureLifecycleService;
|
|
||||||
|
|
||||||
private apiTokenStore: IApiTokenStore;
|
private apiTokenStore: IApiTokenStore;
|
||||||
|
|
||||||
private versionService: VersionService;
|
private versionService: VersionService;
|
||||||
@ -154,7 +149,6 @@ export class InstanceStatsService {
|
|||||||
versionService: VersionService,
|
versionService: VersionService,
|
||||||
getActiveUsers: GetActiveUsers,
|
getActiveUsers: GetActiveUsers,
|
||||||
getProductionChanges: GetProductionChanges,
|
getProductionChanges: GetProductionChanges,
|
||||||
featureLifecycleService: FeatureLifecycleService,
|
|
||||||
) {
|
) {
|
||||||
this.strategyStore = strategyStore;
|
this.strategyStore = strategyStore;
|
||||||
this.userStore = userStore;
|
this.userStore = userStore;
|
||||||
@ -169,7 +163,6 @@ export class InstanceStatsService {
|
|||||||
this.settingStore = settingStore;
|
this.settingStore = settingStore;
|
||||||
this.eventStore = eventStore;
|
this.eventStore = eventStore;
|
||||||
this.clientInstanceStore = clientInstanceStore;
|
this.clientInstanceStore = clientInstanceStore;
|
||||||
this.featureLifecycleService = featureLifecycleService;
|
|
||||||
this.logger = getLogger('services/stats-service.js');
|
this.logger = getLogger('services/stats-service.js');
|
||||||
this.getActiveUsers = getActiveUsers;
|
this.getActiveUsers = getActiveUsers;
|
||||||
this.getProductionChanges = getProductionChanges;
|
this.getProductionChanges = getProductionChanges;
|
||||||
@ -257,7 +250,6 @@ export class InstanceStatsService {
|
|||||||
featureImports,
|
featureImports,
|
||||||
productionChanges,
|
productionChanges,
|
||||||
previousDayMetricsBucketsCount,
|
previousDayMetricsBucketsCount,
|
||||||
featureLifeCycles,
|
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getToggleCount(),
|
this.getToggleCount(),
|
||||||
this.getArchivedToggleCount(),
|
this.getArchivedToggleCount(),
|
||||||
@ -281,7 +273,6 @@ export class InstanceStatsService {
|
|||||||
this.eventStore.filteredCount({ type: FEATURES_IMPORTED }),
|
this.eventStore.filteredCount({ type: FEATURES_IMPORTED }),
|
||||||
this.getProductionChanges(),
|
this.getProductionChanges(),
|
||||||
this.clientMetricsStore.countPreviousDayHourlyMetricsBuckets(),
|
this.clientMetricsStore.countPreviousDayHourlyMetricsBuckets(),
|
||||||
this.getAllWithStageDuration(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -314,7 +305,6 @@ export class InstanceStatsService {
|
|||||||
featureImports,
|
featureImports,
|
||||||
productionChanges,
|
productionChanges,
|
||||||
previousDayMetricsBucketsCount,
|
previousDayMetricsBucketsCount,
|
||||||
featureLifeCycles,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,11 +338,4 @@ export class InstanceStatsService {
|
|||||||
);
|
);
|
||||||
return { ...instanceStats, sum, projects: totalProjects };
|
return { ...instanceStats, sum, projects: totalProjects };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllWithStageDuration(): Promise<IProjectLifecycleStageDuration[]> {
|
|
||||||
if (this.flagResolver.isEnabled('featureLifecycleMetrics')) {
|
|
||||||
return this.featureLifecycleService.getAllWithStageDuration();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,19 @@ import { InstanceStatsService } from './features/instance-stats/instance-stats-s
|
|||||||
import VersionService from './services/version-service';
|
import VersionService from './services/version-service';
|
||||||
import { createFakeGetActiveUsers } from './features/instance-stats/getActiveUsers';
|
import { createFakeGetActiveUsers } from './features/instance-stats/getActiveUsers';
|
||||||
import { createFakeGetProductionChanges } from './features/instance-stats/getProductionChanges';
|
import { createFakeGetProductionChanges } from './features/instance-stats/getProductionChanges';
|
||||||
import type { IEnvironmentStore, IUnleashStores } from './types';
|
import type {
|
||||||
|
IEnvironmentStore,
|
||||||
|
IFeatureLifecycleReadModel,
|
||||||
|
IFeatureLifecycleStore,
|
||||||
|
IUnleashStores,
|
||||||
|
} from './types';
|
||||||
import FakeEnvironmentStore from './features/project-environments/fake-environment-store';
|
import FakeEnvironmentStore from './features/project-environments/fake-environment-store';
|
||||||
import { SchedulerService } from './services';
|
import { SchedulerService } from './services';
|
||||||
import noLogger from '../test/fixtures/no-logger';
|
import noLogger from '../test/fixtures/no-logger';
|
||||||
import { createFeatureLifecycleService } from './features';
|
|
||||||
import dbInit, { type ITestDb } from '../test/e2e/helpers/database-init';
|
|
||||||
import getLogger from '../test/fixtures/no-logger';
|
import getLogger from '../test/fixtures/no-logger';
|
||||||
import type { FeatureLifecycleStore } from './features/feature-lifecycle/feature-lifecycle-store';
|
import dbInit, { type ITestDb } from '../test/e2e/helpers/database-init';
|
||||||
|
import { FeatureLifecycleStore } from './features/feature-lifecycle/feature-lifecycle-store';
|
||||||
|
import { FeatureLifecycleReadModel } from './features/feature-lifecycle/feature-lifecycle-read-model';
|
||||||
|
|
||||||
const monitor = createMetricsMonitor();
|
const monitor = createMetricsMonitor();
|
||||||
const eventBus = new EventEmitter();
|
const eventBus = new EventEmitter();
|
||||||
@ -33,7 +38,8 @@ let environmentStore: IEnvironmentStore;
|
|||||||
let statsService: InstanceStatsService;
|
let statsService: InstanceStatsService;
|
||||||
let stores: IUnleashStores;
|
let stores: IUnleashStores;
|
||||||
let schedulerService: SchedulerService;
|
let schedulerService: SchedulerService;
|
||||||
let featureLifeCycleStore: FeatureLifecycleStore;
|
let featureLifeCycleStore: IFeatureLifecycleStore;
|
||||||
|
let featureLifeCycleReadModel: IFeatureLifecycleReadModel;
|
||||||
let db: ITestDb;
|
let db: ITestDb;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -59,8 +65,13 @@ beforeAll(async () => {
|
|||||||
);
|
);
|
||||||
db = await dbInit('metrics_test', getLogger);
|
db = await dbInit('metrics_test', getLogger);
|
||||||
|
|
||||||
const { featureLifecycleService, featureLifecycleStore } =
|
featureLifeCycleReadModel = new FeatureLifecycleReadModel(
|
||||||
createFeatureLifecycleService(db.rawDatabase, config);
|
db.rawDatabase,
|
||||||
|
config.flagResolver,
|
||||||
|
);
|
||||||
|
stores.featureLifecycleReadModel = featureLifeCycleReadModel;
|
||||||
|
featureLifeCycleStore = new FeatureLifecycleStore(db.rawDatabase);
|
||||||
|
stores.featureLifecycleStore = featureLifeCycleStore;
|
||||||
|
|
||||||
statsService = new InstanceStatsService(
|
statsService = new InstanceStatsService(
|
||||||
stores,
|
stores,
|
||||||
@ -68,9 +79,7 @@ beforeAll(async () => {
|
|||||||
versionService,
|
versionService,
|
||||||
createFakeGetActiveUsers(),
|
createFakeGetActiveUsers(),
|
||||||
createFakeGetProductionChanges(),
|
createFakeGetProductionChanges(),
|
||||||
featureLifecycleService,
|
|
||||||
);
|
);
|
||||||
featureLifeCycleStore = featureLifecycleStore;
|
|
||||||
|
|
||||||
schedulerService = new SchedulerService(
|
schedulerService = new SchedulerService(
|
||||||
noLogger,
|
noLogger,
|
||||||
@ -303,9 +312,13 @@ test('should collect metrics for lifecycle', async () => {
|
|||||||
stage: 'initial',
|
stage: 'initial',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const { featureLifeCycles } = await statsService.getStats();
|
const stageCount = await featureLifeCycleReadModel.getStageCountByProject();
|
||||||
expect(featureLifeCycles).toHaveLength(1);
|
const stageDurations =
|
||||||
|
await featureLifeCycleReadModel.getAllWithStageDuration();
|
||||||
|
expect(stageCount).toHaveLength(1);
|
||||||
|
expect(stageDurations).toHaveLength(1);
|
||||||
|
|
||||||
const metrics = await prometheusRegister.metrics();
|
const metrics = await prometheusRegister.metrics();
|
||||||
expect(metrics).toMatch(/feature_lifecycle_stage_duration/);
|
expect(metrics).toMatch(/feature_lifecycle_stage_duration/);
|
||||||
|
expect(metrics).toMatch(/stage_count_by_project/);
|
||||||
});
|
});
|
||||||
|
@ -306,12 +306,14 @@ export default class MetricsMonitor {
|
|||||||
maxConstraintValuesResult,
|
maxConstraintValuesResult,
|
||||||
maxConstraintsPerStrategyResult,
|
maxConstraintsPerStrategyResult,
|
||||||
stageCountByProjectResult,
|
stageCountByProjectResult,
|
||||||
|
stageDurationByProject,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
stores.featureStrategiesReadModel.getMaxFeatureStrategies(),
|
stores.featureStrategiesReadModel.getMaxFeatureStrategies(),
|
||||||
stores.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
|
stores.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
|
||||||
stores.featureStrategiesReadModel.getMaxConstraintValues(),
|
stores.featureStrategiesReadModel.getMaxConstraintValues(),
|
||||||
stores.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
|
stores.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
|
||||||
stores.featureLifecycleReadModel.getStageCountByProject(),
|
stores.featureLifecycleReadModel.getStageCountByProject(),
|
||||||
|
stores.featureLifecycleReadModel.getAllWithStageDuration(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
featureFlagsTotal.reset();
|
featureFlagsTotal.reset();
|
||||||
@ -326,7 +328,7 @@ export default class MetricsMonitor {
|
|||||||
serviceAccounts.reset();
|
serviceAccounts.reset();
|
||||||
serviceAccounts.set(stats.serviceAccounts);
|
serviceAccounts.set(stats.serviceAccounts);
|
||||||
|
|
||||||
stats.featureLifeCycles.forEach((stage) => {
|
stageDurationByProject.forEach((stage) => {
|
||||||
featureLifecycleStageDuration
|
featureLifecycleStageDuration
|
||||||
.labels({
|
.labels({
|
||||||
stage: stage.stage,
|
stage: stage.stage,
|
||||||
|
@ -125,13 +125,6 @@ class InstanceAdminController extends Controller {
|
|||||||
variantCount: 100,
|
variantCount: 100,
|
||||||
enabledCount: 200,
|
enabledCount: 200,
|
||||||
},
|
},
|
||||||
featureLifeCycles: [
|
|
||||||
{
|
|
||||||
project: 'default',
|
|
||||||
stage: 'archived',
|
|
||||||
duration: 2000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user