mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-05 17:53:12 +02:00
Merge branch 'main' into fix/flagstats_nan
This commit is contained in:
commit
46d15957b1
@ -1,10 +1,6 @@
|
||||
import type { Db, IUnleashConfig } from '../../server-impl';
|
||||
import FeatureToggleStore from '../feature-toggle/feature-toggle-store';
|
||||
import ProjectStatsStore from '../../db/project-stats-store';
|
||||
import {
|
||||
createFakeFeatureToggleService,
|
||||
createFeatureToggleService,
|
||||
} from '../feature-toggle/createFeatureToggleService';
|
||||
import FakeProjectStore from '../../../test/fixtures/fake-project-store';
|
||||
import FakeFeatureToggleStore from '../feature-toggle/fakes/fake-feature-toggle-store';
|
||||
import FakeProjectStatsStore from '../../../test/fixtures/fake-project-stats-store';
|
||||
@ -14,6 +10,8 @@ import { ProjectInsightsService } from './project-insights-service';
|
||||
import ProjectStore from '../project/project-store';
|
||||
import { ProjectInsightsReadModel } from './project-insights-read-model';
|
||||
import { FakeProjectInsightsReadModel } from './fake-project-insights-read-model';
|
||||
import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store';
|
||||
import FakeFeatureStrategiesStore from '../feature-toggle/fakes/fake-feature-strategies-store';
|
||||
|
||||
export const createProjectInsightsService = (
|
||||
db: Db,
|
||||
@ -35,7 +33,12 @@ export const createProjectInsightsService = (
|
||||
|
||||
const featureTypeStore = new FeatureTypeStore(db, getLogger);
|
||||
const projectStatsStore = new ProjectStatsStore(db, eventBus, getLogger);
|
||||
const featureToggleService = createFeatureToggleService(db, config);
|
||||
const featureStrategiesStore = new FeatureStrategiesStore(
|
||||
db,
|
||||
eventBus,
|
||||
getLogger,
|
||||
flagResolver,
|
||||
);
|
||||
const projectInsightsReadModel = new ProjectInsightsReadModel(db);
|
||||
|
||||
return new ProjectInsightsService(
|
||||
@ -44,8 +47,8 @@ export const createProjectInsightsService = (
|
||||
featureToggleStore,
|
||||
featureTypeStore,
|
||||
projectStatsStore,
|
||||
featureStrategiesStore,
|
||||
},
|
||||
featureToggleService,
|
||||
projectInsightsReadModel,
|
||||
);
|
||||
};
|
||||
@ -57,7 +60,7 @@ export const createFakeProjectInsightsService = (
|
||||
const featureToggleStore = new FakeFeatureToggleStore();
|
||||
const featureTypeStore = new FakeFeatureTypeStore();
|
||||
const projectStatsStore = new FakeProjectStatsStore();
|
||||
const featureToggleService = createFakeFeatureToggleService(config);
|
||||
const featureStrategiesStore = new FakeFeatureStrategiesStore();
|
||||
const projectInsightsReadModel = new FakeProjectInsightsReadModel();
|
||||
|
||||
return new ProjectInsightsService(
|
||||
@ -66,8 +69,8 @@ export const createFakeProjectInsightsService = (
|
||||
featureToggleStore,
|
||||
featureTypeStore,
|
||||
projectStatsStore,
|
||||
featureStrategiesStore,
|
||||
},
|
||||
featureToggleService,
|
||||
projectInsightsReadModel,
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,66 @@
|
||||
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
|
||||
import getLogger from '../../../test/fixtures/no-logger';
|
||||
import type { IUser } from '../../types';
|
||||
import type { IProjectInsightsReadModel } from './project-insights-read-model-type';
|
||||
import {
|
||||
type ChangeRequestDBState,
|
||||
ProjectInsightsReadModel,
|
||||
} from './project-insights-read-model';
|
||||
|
||||
let projectInsightsReadModel: IProjectInsightsReadModel;
|
||||
let user: IUser;
|
||||
let db: ITestDb;
|
||||
const projectId = 'default';
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('project_insights_read_model', getLogger);
|
||||
projectInsightsReadModel = new ProjectInsightsReadModel(db.rawDatabase);
|
||||
user = await db.stores.userStore.insert({
|
||||
username: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.rawDatabase.table('change_requests').delete();
|
||||
});
|
||||
|
||||
const createChangeRequest = (id: number, state: string) =>
|
||||
db.rawDatabase.table('change_requests').insert({
|
||||
id,
|
||||
state,
|
||||
environment: 'default',
|
||||
project: projectId,
|
||||
created_by: user.id,
|
||||
});
|
||||
|
||||
test('can read change request status counts', async () => {
|
||||
const states: ChangeRequestDBState[] = [
|
||||
'Approved',
|
||||
'Approved',
|
||||
'Applied',
|
||||
'Rejected',
|
||||
'Scheduled',
|
||||
'In review',
|
||||
'Draft',
|
||||
'Cancelled',
|
||||
];
|
||||
await Promise.all(
|
||||
states.map((state, id) => createChangeRequest(id, state)),
|
||||
);
|
||||
|
||||
const changeRequests =
|
||||
await projectInsightsReadModel.getChangeRequests(projectId);
|
||||
|
||||
expect(changeRequests).toEqual({
|
||||
total: 6,
|
||||
approved: 2,
|
||||
applied: 1,
|
||||
rejected: 1,
|
||||
reviewRequired: 1,
|
||||
scheduled: 1,
|
||||
});
|
||||
});
|
@ -5,6 +5,8 @@ import type {
|
||||
import type { Db } from '../../db/db';
|
||||
|
||||
export type ChangeRequestDBState =
|
||||
| 'Draft'
|
||||
| 'Cancelled'
|
||||
| 'Approved'
|
||||
| 'In review'
|
||||
| 'Applied'
|
||||
|
@ -1,11 +1,11 @@
|
||||
import type {
|
||||
IFeatureOverview,
|
||||
IFeatureStrategiesStore,
|
||||
IFeatureToggleStore,
|
||||
IFeatureTypeStore,
|
||||
IProjectHealth,
|
||||
IProjectStore,
|
||||
IUnleashStores,
|
||||
} from '../../types';
|
||||
import type FeatureToggleService from '../feature-toggle/feature-toggle-service';
|
||||
import { calculateAverageTimeToProd } from '../feature-toggle/time-to-production/time-to-production';
|
||||
import type { IProjectStatsStore } from '../../types/stores/project-stats-store-type';
|
||||
import type { ProjectDoraMetricsSchema } from '../../openapi';
|
||||
@ -19,7 +19,7 @@ export class ProjectInsightsService {
|
||||
|
||||
private featureTypeStore: IFeatureTypeStore;
|
||||
|
||||
private featureToggleService: FeatureToggleService;
|
||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||
|
||||
private projectStatsStore: IProjectStatsStore;
|
||||
|
||||
@ -31,20 +31,21 @@ export class ProjectInsightsService {
|
||||
featureToggleStore,
|
||||
featureTypeStore,
|
||||
projectStatsStore,
|
||||
featureStrategiesStore,
|
||||
}: Pick<
|
||||
IUnleashStores,
|
||||
| 'projectStore'
|
||||
| 'featureToggleStore'
|
||||
| 'projectStatsStore'
|
||||
| 'featureTypeStore'
|
||||
| 'featureStrategiesStore'
|
||||
>,
|
||||
featureToggleService: FeatureToggleService,
|
||||
projectInsightsReadModel: IProjectInsightsReadModel,
|
||||
) {
|
||||
this.projectStore = projectStore;
|
||||
this.featureToggleStore = featureToggleStore;
|
||||
this.featureTypeStore = featureTypeStore;
|
||||
this.featureToggleService = featureToggleService;
|
||||
this.featureStrategiesStore = featureStrategiesStore;
|
||||
this.projectStatsStore = projectStatsStore;
|
||||
this.projectInsightsReadModel = projectInsightsReadModel;
|
||||
}
|
||||
@ -101,6 +102,26 @@ export class ProjectInsightsService {
|
||||
};
|
||||
}
|
||||
|
||||
private async getProjectHealth(
|
||||
projectId: string,
|
||||
archived: boolean = false,
|
||||
userId?: number,
|
||||
): Promise<{ health: number; features: IFeatureOverview[] }> {
|
||||
const [project, features] = await Promise.all([
|
||||
this.projectStore.get(projectId),
|
||||
this.featureStrategiesStore.getFeatureOverview({
|
||||
projectId,
|
||||
archived,
|
||||
userId,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
health: project.health || 0,
|
||||
features: features,
|
||||
};
|
||||
}
|
||||
|
||||
async getProjectInsights(projectId: string) {
|
||||
const result = {
|
||||
members: {
|
||||
@ -112,7 +133,7 @@ export class ProjectInsightsService {
|
||||
const [stats, featureTypeCounts, health, leadTime, changeRequests] =
|
||||
await Promise.all([
|
||||
this.projectStatsStore.getProjectStats(projectId),
|
||||
this.featureToggleService.getFeatureTypeCounts({
|
||||
this.featureToggleStore.getFeatureTypeCounts({
|
||||
projectId,
|
||||
archived: false,
|
||||
}),
|
||||
@ -130,40 +151,4 @@ export class ProjectInsightsService {
|
||||
changeRequests,
|
||||
};
|
||||
}
|
||||
|
||||
private async getProjectHealth(
|
||||
projectId: string,
|
||||
archived: boolean = false,
|
||||
userId?: number,
|
||||
): Promise<IProjectHealth> {
|
||||
const [project, environments, features, members, projectStats] =
|
||||
await Promise.all([
|
||||
this.projectStore.get(projectId),
|
||||
this.projectStore.getEnvironmentsForProject(projectId),
|
||||
this.featureToggleService.getFeatureOverview({
|
||||
projectId,
|
||||
archived,
|
||||
userId,
|
||||
}),
|
||||
this.projectStore.getMembersCountByProject(projectId),
|
||||
this.projectStatsStore.getProjectStats(projectId),
|
||||
]);
|
||||
|
||||
return {
|
||||
stats: projectStats,
|
||||
name: project.name,
|
||||
description: project.description!,
|
||||
mode: project.mode,
|
||||
featureLimit: project.featureLimit,
|
||||
featureNaming: project.featureNaming,
|
||||
defaultStickiness: project.defaultStickiness,
|
||||
health: project.health || 0,
|
||||
updatedAt: project.updatedAt,
|
||||
createdAt: project.createdAt,
|
||||
environments,
|
||||
features: features,
|
||||
members,
|
||||
version: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -34,8 +34,6 @@ test('project insights happy path', async () => {
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
console.log(body);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
stats: {
|
||||
avgTimeToProdCurrentWindow: 0,
|
||||
|
Loading…
Reference in New Issue
Block a user