1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-09-05 17:53:12 +02:00

feat: update backend to include technicalDebt field (#10088)

Deprecate `health` for a more descriptive `technicalDebt` in project-related services and schemas.
This commit is contained in:
Tymoteusz Czech 2025-06-06 16:27:41 +02:00 committed by GitHub
parent c739ea71cf
commit a5e5ea0436
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 183 additions and 15 deletions

View File

@ -13,9 +13,13 @@ export type BasePersonalProject = {
export type PersonalProject = BasePersonalProject & { export type PersonalProject = BasePersonalProject & {
owners?: ProjectOwners; owners?: ProjectOwners;
} & { } & {
health: number; technicalDebt: number;
memberCount: number; memberCount: number;
featureCount: number; featureCount: number;
/**
* @deprecated
*/
health: number;
}; };
export interface IPersonalDashboardReadModel { export interface IPersonalDashboardReadModel {

View File

@ -96,6 +96,7 @@ export class PersonalDashboardService {
id: project.id, id: project.id,
name: project.name, name: project.name,
health: project.health, health: project.health,
technicalDebt: 100 - (project.health || 0),
memberCount: project.memberCount, memberCount: project.memberCount,
featureCount: project.featureCount, featureCount: project.featureCount,
})); }));
@ -193,6 +194,7 @@ export class PersonalDashboardService {
projectInsights?.potentiallyStaleFeatureCount || 0; projectInsights?.potentiallyStaleFeatureCount || 0;
const staleFlags = projectInsights?.staleFeatureCount || 0; const staleFlags = projectInsights?.staleFeatureCount || 0;
const currentHealth = projectInsights?.health || 0; const currentHealth = projectInsights?.health || 0;
const technicalDebt = projectInsights?.technicalDebt || 0;
return { return {
latestEvents, latestEvents,
@ -206,6 +208,10 @@ export class PersonalDashboardService {
potentiallyStaleFlags, potentiallyStaleFlags,
staleFlags, staleFlags,
activeFlags: totalFlags - staleFlags - potentiallyStaleFlags, activeFlags: totalFlags - staleFlags - potentiallyStaleFlags,
technicalDebt,
/**
* @deprecated
*/
health: currentHealth, health: currentHealth,
}, },
}; };

View File

@ -47,6 +47,12 @@ test('Return basic insights', async () => {
staleCount: 0, staleCount: 0,
rating: 100, rating: 100,
}, },
technicalDebt: {
activeCount: 0,
potentiallyStaleCount: 0,
staleCount: 0,
rating: 0,
},
leadTime: { features: [], projectAverage: 0 }, leadTime: { features: [], projectAverage: 0 },
members: { currentMembers: 0, change: 0 }, members: { currentMembers: 0, change: 0 },
}); });

View File

@ -93,6 +93,10 @@ export class ProjectInsightsService {
activeCount, activeCount,
potentiallyStaleCount, potentiallyStaleCount,
staleCount, staleCount,
technicalDebt: overview.technicalDebt,
/**
* @deprecated
*/
rating: overview.health, rating: overview.health,
}; };
} }
@ -101,7 +105,14 @@ export class ProjectInsightsService {
projectId: string, projectId: string,
archived: boolean = false, archived: boolean = false,
userId?: number, userId?: number,
): Promise<{ health: number; features: IFeatureOverview[] }> { ): Promise<{
technicalDebt: number;
features: IFeatureOverview[];
/**
* @deprecated
*/
health: number;
}> {
const [project, features] = await Promise.all([ const [project, features] = await Promise.all([
this.projectStore.get(projectId), this.projectStore.get(projectId),
this.featureStrategiesStore.getFeatureOverview({ this.featureStrategiesStore.getFeatureOverview({
@ -113,6 +124,7 @@ export class ProjectInsightsService {
return { return {
health: project?.health || 0, health: project?.health || 0,
technicalDebt: 100 - (project?.health || 0),
features: features, features: features,
}; };
} }
@ -151,9 +163,23 @@ export class ProjectInsightsService {
return { return {
stats, stats,
featureTypeCounts, featureTypeCounts,
health, technicalDebt: {
rating: health.technicalDebt,
activeCount: health.activeCount,
potentiallyStaleCount: health.potentiallyStaleCount,
staleCount: health.staleCount,
},
leadTime, leadTime,
members, members,
/**
* @deprecated
*/
health: {
rating: health.rating,
activeCount: health.activeCount,
potentiallyStaleCount: health.potentiallyStaleCount,
staleCount: health.staleCount,
},
}; };
} }
} }

View File

@ -88,6 +88,9 @@ export class ProjectStatusService {
health: { health: {
current: currentHealth, current: currentHealth,
}, },
technicalDebt: {
current: 100 - currentHealth,
},
lifecycleSummary, lifecycleSummary,
staleFlags: { staleFlags: {
total: staleFlagCount, total: staleFlagCount,

View File

@ -18,12 +18,16 @@ export type ProjectForUi = {
export type ProjectForInsights = { export type ProjectForInsights = {
id: string; id: string;
health: number; technicalDebt: number;
memberCount: number; memberCount: number;
featureCount: number; featureCount: number;
staleFeatureCount: number; staleFeatureCount: number;
potentiallyStaleFeatureCount: number; potentiallyStaleFeatureCount: number;
avgTimeToProduction: number; avgTimeToProduction: number;
/**
* @deprecated
*/
health: number;
}; };
export interface IProjectReadModel { export interface IProjectReadModel {

View File

@ -42,6 +42,7 @@ const mapProjectForInsights = (row): ProjectForInsights => {
Number(row.potentially_stale_feature_count) || 0, Number(row.potentially_stale_feature_count) || 0,
memberCount: Number(row.number_of_users) || 0, memberCount: Number(row.number_of_users) || 0,
avgTimeToProduction: row.avg_time_to_prod_current_window || 0, avgTimeToProduction: row.avg_time_to_prod_current_window || 0,
technicalDebt: 100 - (row.health || 0),
}; };
}; };

View File

@ -1280,6 +1280,7 @@ export default class ProjectService {
featureNaming: project.featureNaming, featureNaming: project.featureNaming,
defaultStickiness: project.defaultStickiness, defaultStickiness: project.defaultStickiness,
health: project.health || 0, health: project.health || 0,
technicalDebt: 100 - (project.health || 0),
favorite: favorite, favorite: favorite,
updatedAt: project.updatedAt, updatedAt: project.updatedAt,
createdAt: project.createdAt, createdAt: project.createdAt,
@ -1338,6 +1339,7 @@ export default class ProjectService {
linkTemplates: project.linkTemplates, linkTemplates: project.linkTemplates,
defaultStickiness: project.defaultStickiness, defaultStickiness: project.defaultStickiness,
health: project.health || 0, health: project.health || 0,
technicalDebt: 100 - (project.health || 0),
favorite: favorite, favorite: favorite,
updatedAt: project.updatedAt, updatedAt: project.updatedAt,
archivedAt: project.archivedAt, archivedAt: project.archivedAt,

View File

@ -26,6 +26,7 @@ export const healthOverviewSchema = {
'mode', 'mode',
'members', 'members',
'health', 'health',
'technicalDebt',
'environments', 'environments',
'features', 'features',
], ],
@ -75,9 +76,17 @@ export const healthOverviewSchema = {
}, },
health: { health: {
type: 'integer', type: 'integer',
description: description: 'Use `technicalDebt` instead.',
'The overall [health rating](https://docs.getunleash.io/reference/technical-debt#project-status) of the project.',
example: 95, example: 95,
deprecated: true,
},
technicalDebt: {
type: 'number',
example: 25,
minimum: 0,
maximum: 100,
description:
"An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
}, },
environments: { environments: {
type: 'array', type: 'array',

View File

@ -28,6 +28,7 @@ export const personalDashboardProjectDetailsSchema = {
'staleFlags', 'staleFlags',
'potentiallyStaleFlags', 'potentiallyStaleFlags',
'health', 'health',
'technicalDebt',
], ],
properties: { properties: {
avgHealthCurrentWindow: { avgHealthCurrentWindow: {
@ -76,8 +77,17 @@ export const personalDashboardProjectDetailsSchema = {
health: { health: {
type: 'integer', type: 'integer',
minimum: 0, minimum: 0,
description: "The project's current health score", description: 'Use `technicalDebt` instead.',
example: 80, example: 80,
deprecated: true,
},
technicalDebt: {
type: 'integer',
example: 25,
minimum: 0,
maximum: 100,
description:
"An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
}, },
}, },
}, },

View File

@ -84,6 +84,7 @@ export const personalDashboardSchema = {
'id', 'id',
'name', 'name',
'health', 'health',
'technicalDebt',
'memberCount', 'memberCount',
'featureCount', 'featureCount',
], ],
@ -102,8 +103,16 @@ export const personalDashboardSchema = {
type: 'integer', type: 'integer',
example: 50, example: 50,
minimum: 0, minimum: 0,
deprecated: true,
description: 'Use `technicalDebt` instead.',
},
technicalDebt: {
type: 'integer',
example: 25,
minimum: 0,
maximum: 100,
description: description:
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100", "An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
}, },
memberCount: { memberCount: {
type: 'integer', type: 'integer',

View File

@ -8,7 +8,14 @@ export const projectInsightsSchema = {
$id: '#/components/schemas/projectInsightsSchema', $id: '#/components/schemas/projectInsightsSchema',
type: 'object', type: 'object',
additionalProperties: false, additionalProperties: false,
required: ['stats', 'leadTime', 'featureTypeCounts', 'health', 'members'], required: [
'stats',
'leadTime',
'featureTypeCounts',
'health',
'technicalDebt',
'members',
],
description: description:
'A high-level overview of a project insights. It contains information such as project statistics, overall health, types of flags, members overview, change requests overview.', 'A high-level overview of a project insights. It contains information such as project statistics, overall health, types of flags, members overview, change requests overview.',
properties: { properties: {
@ -18,6 +25,7 @@ export const projectInsightsSchema = {
}, },
health: { health: {
type: 'object', type: 'object',
deprecated: true,
required: [ required: [
'rating', 'rating',
'activeCount', 'activeCount',
@ -28,7 +36,7 @@ export const projectInsightsSchema = {
rating: { rating: {
type: 'integer', type: 'integer',
description: description:
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100", "An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
example: 95, example: 95,
}, },
activeCount: { activeCount: {
@ -48,7 +56,44 @@ export const projectInsightsSchema = {
example: 10, example: 10,
}, },
}, },
description: 'Health summary of the project', description:
'Use `technicalDebt` instead. Summary of the project health',
},
technicalDebt: {
type: 'object',
required: [
'rating',
'activeCount',
'potentiallyStaleCount',
'staleCount',
],
properties: {
rating: {
type: 'integer',
description:
"An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
example: 25,
minimum: 0,
maximum: 100,
},
activeCount: {
type: 'number',
description: 'The number of active feature flags.',
example: 12,
},
potentiallyStaleCount: {
type: 'number',
description:
'The number of potentially stale feature flags.',
example: 5,
},
staleCount: {
type: 'number',
description: 'The number of stale feature flags.',
example: 10,
},
},
description: 'Summary of the projects technical debt',
}, },
leadTime: { leadTime: {
type: 'object', type: 'object',

View File

@ -84,8 +84,16 @@ export const projectOverviewSchema = {
health: { health: {
type: 'number', type: 'number',
example: 50, example: 50,
deprecated: true,
description: 'Use `technicalDebt` instead.',
},
technicalDebt: {
type: 'number',
example: 25,
minimum: 0,
maximum: 100,
description: description:
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100", "An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
}, },
environments: { environments: {
type: 'array', type: 'array',

View File

@ -28,8 +28,16 @@ export const projectSchema = {
health: { health: {
type: 'number', type: 'number',
example: 50, example: 50,
description: 'Use `technicalDebt` instead.',
deprecated: true,
},
technicalDebt: {
type: 'number',
example: 25,
minimum: 0,
maximum: 100,
description: description:
"An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100", "An indicator of the [project's technical debt](https://docs.getunleash.io/reference/technical-debt#project-status) on a scale from 0 to 100",
}, },
featureCount: { featureCount: {
type: 'number', type: 'number',

View File

@ -6,6 +6,9 @@ test('projectStatusSchema', () => {
health: { health: {
current: 50, current: 50,
}, },
technicalDebt: {
current: 50,
},
lifecycleSummary: { lifecycleSummary: {
initial: { initial: {
currentFlags: 0, currentFlags: 0,

View File

@ -32,6 +32,7 @@ export const projectStatusSchema = {
'activityCountByDate', 'activityCountByDate',
'resources', 'resources',
'health', 'health',
'technicalDebt',
'lifecycleSummary', 'lifecycleSummary',
'staleFlags', 'staleFlags',
], ],
@ -57,6 +58,21 @@ export const projectStatusSchema = {
}, },
}, },
}, },
technicalDebt: {
type: 'object',
additionalProperties: false,
required: ['current'],
description: "Information about the project's health rating",
properties: {
current: {
type: 'integer',
minimum: 0,
maximum: 100,
description: `The project's current health score, based on the ratio of healthy flags to stale and potentially stale flags.`,
example: 100,
},
},
},
resources: { resources: {
type: 'object', type: 'object',
additionalProperties: false, additionalProperties: false,

View File

@ -342,7 +342,7 @@ export interface IProjectHealth {
features: IFeatureOverview[]; features: IFeatureOverview[];
members: number; members: number;
version: number; version: number;
health: number; technicalDebt: number;
favorite?: boolean; favorite?: boolean;
updatedAt?: Date; updatedAt?: Date;
createdAt: Date | undefined; createdAt: Date | undefined;
@ -351,6 +351,10 @@ export interface IProjectHealth {
featureLimit?: number; featureLimit?: number;
featureNaming?: IFeatureNaming; featureNaming?: IFeatureNaming;
defaultStickiness: string; defaultStickiness: string;
/**
* @deprecated
*/
health: number;
} }
export type ProjectOnboardingStatus = export type ProjectOnboardingStatus =
@ -366,7 +370,7 @@ export interface IProjectOverview {
featureTypeCounts: IFeatureTypeCount[]; featureTypeCounts: IFeatureTypeCount[];
members: number; members: number;
version: number; version: number;
health: number; technicalDebt: number;
favorite?: boolean; favorite?: boolean;
updatedAt?: Date; updatedAt?: Date;
archivedAt?: Date; archivedAt?: Date;
@ -378,6 +382,10 @@ export interface IProjectOverview {
defaultStickiness: string; defaultStickiness: string;
onboardingStatus: ProjectOnboardingStatus; onboardingStatus: ProjectOnboardingStatus;
linkTemplates?: IProjectLinkTemplate[]; linkTemplates?: IProjectLinkTemplate[];
/**
* @deprecated
*/
health: number;
} }
export interface IProjectHealthReport extends IProjectHealth { export interface IProjectHealthReport extends IProjectHealth {