mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat(1-3085): hook up lifecycle read model data to endpoint (#8709)
This PR hooks up the project lifecycle summary read model to the service and exposes the lifecycle summary data in the controller.
This commit is contained in:
parent
92f7acbfc4
commit
8493bee272
@ -10,6 +10,10 @@ import SegmentStore from '../segment/segment-store';
|
||||
import FakeSegmentStore from '../../../test/fixtures/fake-segment-store';
|
||||
import { PersonalDashboardReadModel } from '../personal-dashboard/personal-dashboard-read-model';
|
||||
import { FakePersonalDashboardReadModel } from '../personal-dashboard/fake-personal-dashboard-read-model';
|
||||
import {
|
||||
createFakeProjectLifecycleSummaryReadModel,
|
||||
createProjectLifecycleSummaryReadModel,
|
||||
} from './project-lifecycle-read-model/createProjectLifecycleSummaryReadModel';
|
||||
|
||||
export const createProjectStatusService = (
|
||||
db: Db,
|
||||
@ -34,6 +38,8 @@ export const createProjectStatusService = (
|
||||
config.getLogger,
|
||||
config.flagResolver,
|
||||
);
|
||||
const projectLifecycleSummaryReadModel =
|
||||
createProjectLifecycleSummaryReadModel(db, config);
|
||||
|
||||
return new ProjectStatusService(
|
||||
{
|
||||
@ -43,6 +49,7 @@ export const createProjectStatusService = (
|
||||
segmentStore,
|
||||
},
|
||||
new PersonalDashboardReadModel(db),
|
||||
projectLifecycleSummaryReadModel,
|
||||
);
|
||||
};
|
||||
|
||||
@ -59,6 +66,7 @@ export const createFakeProjectStatusService = () => {
|
||||
segmentStore,
|
||||
},
|
||||
new FakePersonalDashboardReadModel(),
|
||||
createFakeProjectLifecycleSummaryReadModel(),
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -7,6 +7,7 @@ import type {
|
||||
IUnleashStores,
|
||||
} from '../../types';
|
||||
import type { IPersonalDashboardReadModel } from '../personal-dashboard/personal-dashboard-read-model-type';
|
||||
import type { IProjectLifecycleSummaryReadModel } from './project-lifecycle-read-model/project-lifecycle-read-model-type';
|
||||
|
||||
export class ProjectStatusService {
|
||||
private eventStore: IEventStore;
|
||||
@ -14,6 +15,7 @@ export class ProjectStatusService {
|
||||
private apiTokenStore: IApiTokenStore;
|
||||
private segmentStore: ISegmentStore;
|
||||
private personalDashboardReadModel: IPersonalDashboardReadModel;
|
||||
private projectLifecycleSummaryReadModel: IProjectLifecycleSummaryReadModel;
|
||||
|
||||
constructor(
|
||||
{
|
||||
@ -26,12 +28,14 @@ export class ProjectStatusService {
|
||||
'eventStore' | 'projectStore' | 'apiTokenStore' | 'segmentStore'
|
||||
>,
|
||||
personalDashboardReadModel: IPersonalDashboardReadModel,
|
||||
projectLifecycleReadModel: IProjectLifecycleSummaryReadModel,
|
||||
) {
|
||||
this.eventStore = eventStore;
|
||||
this.projectStore = projectStore;
|
||||
this.apiTokenStore = apiTokenStore;
|
||||
this.segmentStore = segmentStore;
|
||||
this.personalDashboardReadModel = personalDashboardReadModel;
|
||||
this.projectLifecycleSummaryReadModel = projectLifecycleReadModel;
|
||||
}
|
||||
|
||||
async getProjectStatus(projectId: string): Promise<ProjectStatusSchema> {
|
||||
@ -42,6 +46,7 @@ export class ProjectStatusService {
|
||||
segments,
|
||||
activityCountByDate,
|
||||
healthScores,
|
||||
lifecycleSummary,
|
||||
] = await Promise.all([
|
||||
this.projectStore.getConnectedEnvironmentCountForProject(projectId),
|
||||
this.projectStore.getMembersCountByProject(projectId),
|
||||
@ -49,6 +54,9 @@ export class ProjectStatusService {
|
||||
this.segmentStore.getProjectSegmentCount(projectId),
|
||||
this.eventStore.getProjectRecentEventActivity(projectId),
|
||||
this.personalDashboardReadModel.getLatestHealthScores(projectId, 4),
|
||||
this.projectLifecycleSummaryReadModel.getProjectLifecycleSummary(
|
||||
projectId,
|
||||
),
|
||||
]);
|
||||
|
||||
const averageHealth = healthScores.length
|
||||
@ -65,6 +73,7 @@ export class ProjectStatusService {
|
||||
},
|
||||
activityCountByDate,
|
||||
averageHealth,
|
||||
lifecycleSummary,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -256,3 +256,33 @@ test('project health should be correct average', async () => {
|
||||
|
||||
expect(body.averageHealth).toBe(40);
|
||||
});
|
||||
|
||||
test('project status contains lifecycle data', async () => {
|
||||
const { body } = await app.request
|
||||
.get('/api/admin/projects/default/status')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body.lifecycleSummary).toMatchObject({
|
||||
initial: {
|
||||
averageDays: null,
|
||||
currentFlags: 0,
|
||||
},
|
||||
preLive: {
|
||||
averageDays: null,
|
||||
currentFlags: 0,
|
||||
},
|
||||
live: {
|
||||
averageDays: null,
|
||||
currentFlags: 0,
|
||||
},
|
||||
completed: {
|
||||
averageDays: null,
|
||||
currentFlags: 0,
|
||||
},
|
||||
archived: {
|
||||
currentFlags: 0,
|
||||
last30Days: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,29 @@
|
||||
import type { FromSchema } from 'json-schema-to-ts';
|
||||
import { projectActivitySchema } from './project-activity-schema';
|
||||
|
||||
const stageDataWithAverageDaysSchema = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
description:
|
||||
'Statistics on feature flags in a given stage in this project.',
|
||||
required: ['averageDays', 'currentFlags'],
|
||||
properties: {
|
||||
averageDays: {
|
||||
type: 'number',
|
||||
nullable: true,
|
||||
description:
|
||||
"The average number of days a feature flag remains in a stage in this project. Will be null if Unleash doesn't have any data for this stage yet.",
|
||||
example: 5,
|
||||
},
|
||||
currentFlags: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The number of feature flags currently in a stage in this project.',
|
||||
example: 10,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const projectStatusSchema = {
|
||||
$id: '#/components/schemas/projectStatusSchema',
|
||||
type: 'object',
|
||||
@ -57,6 +80,39 @@ export const projectStatusSchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
lifecycleSummary: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
description: 'Feature flag lifecycle statistics for this project.',
|
||||
required: ['initial', 'preLive', 'live', 'completed', 'archived'],
|
||||
properties: {
|
||||
initial: stageDataWithAverageDaysSchema,
|
||||
preLive: stageDataWithAverageDaysSchema,
|
||||
live: stageDataWithAverageDaysSchema,
|
||||
completed: stageDataWithAverageDaysSchema,
|
||||
archived: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['currentFlags', 'last30Days'],
|
||||
description:
|
||||
'Information on archived flags in this project.',
|
||||
properties: {
|
||||
currentFlags: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The number of archived feature flags in this project. If a flag is deleted permanently, it will no longer be counted as part of this statistic.',
|
||||
example: 10,
|
||||
},
|
||||
last30Days: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The number of flags in this project that have been changed over the last 30 days.',
|
||||
example: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
|
Loading…
Reference in New Issue
Block a user