1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: personal dashboard project avg health scores (#8328)

This commit is contained in:
Mateusz Kwasniewski 2024-10-02 09:33:50 +02:00 committed by GitHub
parent a874ac085d
commit f47ae12263
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 117 additions and 1 deletions

View File

@ -8,6 +8,13 @@ import type {
export class FakePersonalDashboardReadModel
implements IPersonalDashboardReadModel
{
async getLatestHealthScores(
project: string,
count: number,
): Promise<number[]> {
return [];
}
async getPersonalFeatures(userId: number): Promise<PersonalFeature[]> {
return [];
}

View File

@ -227,6 +227,10 @@ test('should return personal dashboard project details', async () => {
);
expect(body).toMatchObject({
insights: {
avgHealthCurrentWindow: null,
avgHealthPastWindow: null,
},
owners: [{}],
roles: [{}],
onboardingStatus: {
@ -260,6 +264,41 @@ test('should return personal dashboard project details', async () => {
},
],
});
const insertHealthScore = (id: string, health: number) => {
const irrelevantFlagTrendDetails = {
total_flags: 10,
stale_flags: 10,
potentially_stale_flags: 10,
};
return db.rawDatabase('flag_trends').insert({
...irrelevantFlagTrendDetails,
id,
project: project.id,
health,
});
};
await insertHealthScore('2024-01', 80);
await insertHealthScore('2024-02', 80);
await insertHealthScore('2024-03', 80);
await insertHealthScore('2024-04', 81);
await insertHealthScore('2024-05', 90);
await insertHealthScore('2024-06', 91);
await insertHealthScore('2024-07', 91);
await insertHealthScore('2024-08', 91);
const { body: bodyWithHealthScores } = await app.request.get(
`/api/admin/personal-dashboard/${project.id}`,
);
expect(bodyWithHealthScores).toMatchObject({
insights: {
avgHealthPastWindow: 80,
avgHealthCurrentWindow: 91,
},
});
});
test('should return Unleash admins', async () => {

View File

@ -21,4 +21,5 @@ export type PersonalProject = BasePersonalProject & {
export interface IPersonalDashboardReadModel {
getPersonalFeatures(userId: number): Promise<PersonalFeature[]>;
getPersonalProjects(userId: number): Promise<BasePersonalProject[]>;
getLatestHealthScores(project: string, count: number): Promise<number[]>;
}

View File

@ -19,6 +19,19 @@ export class PersonalDashboardReadModel implements IPersonalDashboardReadModel {
this.db = db;
}
async getLatestHealthScores(
project: string,
count: number,
): Promise<number[]> {
const results = await this.db<{ health: number }>('flag_trends')
.select('health')
.orderBy('created_at', 'desc')
.where('project', project)
.limit(count);
return results.map((row) => Number(row.health));
}
async getPersonalProjects(userId: number): Promise<BasePersonalProject[]> {
const result = await this.db<{
name: string;

View File

@ -136,11 +136,39 @@ export class PersonalDashboardService {
type: role.type as PersonalDashboardProjectDetailsSchema['roles'][number]['type'],
}));
const healthScores =
await this.personalDashboardReadModel.getLatestHealthScores(
projectId,
8,
);
let avgHealthCurrentWindow: number | null = null;
let avgHealthPastWindow: number | null = null;
if (healthScores.length >= 4) {
avgHealthCurrentWindow = Math.round(
healthScores
.slice(0, 4)
.reduce((acc, score) => acc + score, 0) / 4,
);
}
if (healthScores.length >= 8) {
avgHealthPastWindow = Math.round(
healthScores
.slice(4, 8)
.reduce((acc, score) => acc + score, 0) / 4,
);
}
return {
latestEvents: formattedEvents,
onboardingStatus,
owners,
roles: projectRoles,
insights: {
avgHealthCurrentWindow,
avgHealthPastWindow,
},
};
}

View File

@ -7,8 +7,36 @@ export const personalDashboardProjectDetailsSchema = {
type: 'object',
description: 'Project details in personal dashboard',
additionalProperties: false,
required: ['owners', 'roles', 'latestEvents', 'onboardingStatus'],
required: [
'owners',
'roles',
'latestEvents',
'onboardingStatus',
'insights',
],
properties: {
insights: {
type: 'object',
description: 'Insights for the project',
additionalProperties: false,
required: ['avgHealthCurrentWindow', 'avgHealthPastWindow'],
properties: {
avgHealthCurrentWindow: {
type: 'number',
description:
'The average health score in the current window of the last 4 weeks',
example: 80,
nullable: true,
},
avgHealthPastWindow: {
type: 'number',
description:
'The average health score in the previous 4 weeks before the current window',
example: 70,
nullable: true,
},
},
},
onboardingStatus: projectOverviewSchema.properties.onboardingStatus,
latestEvents: {
type: 'array',