mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-19 00:15:43 +01:00
Add project health service
This commit is contained in:
parent
c1c56acf15
commit
8c26410f50
@ -23,6 +23,7 @@ import ResetTokenService from './reset-token-service';
|
|||||||
import SettingService from './setting-service';
|
import SettingService from './setting-service';
|
||||||
import SessionService from './session-service';
|
import SessionService from './session-service';
|
||||||
import UserFeedbackService from './user-feedback-service';
|
import UserFeedbackService from './user-feedback-service';
|
||||||
|
import ProjectHealthService from './project-health-service';
|
||||||
|
|
||||||
export const createServices = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
@ -54,6 +55,7 @@ export const createServices = (
|
|||||||
const healthService = new HealthService(stores, config);
|
const healthService = new HealthService(stores, config);
|
||||||
const settingService = new SettingService(stores, config);
|
const settingService = new SettingService(stores, config);
|
||||||
const userFeedbackService = new UserFeedbackService(stores, config);
|
const userFeedbackService = new UserFeedbackService(stores, config);
|
||||||
|
const projectHealthService = new ProjectHealthService(stores, config);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessService,
|
accessService,
|
||||||
@ -77,6 +79,7 @@ export const createServices = (
|
|||||||
settingService,
|
settingService,
|
||||||
sessionService,
|
sessionService,
|
||||||
userFeedbackService,
|
userFeedbackService,
|
||||||
|
projectHealthService,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
149
src/lib/services/project-health-service.ts
Normal file
149
src/lib/services/project-health-service.ts
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import { IUnleashStores } from '../types/stores';
|
||||||
|
import { IUnleashConfig } from '../types/option';
|
||||||
|
import ProjectStore, { IProject } from '../db/project-store';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import {
|
||||||
|
FeatureToggle,
|
||||||
|
IFeatureOverview,
|
||||||
|
IProjectHealthReport,
|
||||||
|
IProjectOverview,
|
||||||
|
} from '../types/model';
|
||||||
|
import {
|
||||||
|
MILLISECONDS_IN_DAY,
|
||||||
|
MILLISECONDS_IN_ONE_HOUR,
|
||||||
|
} from '../util/constants';
|
||||||
|
import FeatureTypeStore from '../db/feature-type-store';
|
||||||
|
import Timer = NodeJS.Timer;
|
||||||
|
import FeatureToggleStore from '../db/feature-toggle-store';
|
||||||
|
|
||||||
|
export default class ProjectHealthService {
|
||||||
|
private logger: Logger;
|
||||||
|
|
||||||
|
private projectStore: ProjectStore;
|
||||||
|
|
||||||
|
private featureTypeStore: FeatureTypeStore;
|
||||||
|
|
||||||
|
private featureToggleStore: FeatureToggleStore;
|
||||||
|
|
||||||
|
private featureTypes: Map<string, number>;
|
||||||
|
|
||||||
|
private healthRatingTimer: Timer;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
{
|
||||||
|
projectStore,
|
||||||
|
featureTypeStore,
|
||||||
|
featureToggleStore,
|
||||||
|
}: Pick<
|
||||||
|
IUnleashStores,
|
||||||
|
'projectStore' | 'featureTypeStore' | 'featureToggleStore'
|
||||||
|
>,
|
||||||
|
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||||
|
) {
|
||||||
|
this.logger = getLogger('services/project-health-service.ts');
|
||||||
|
this.projectStore = projectStore;
|
||||||
|
this.featureTypeStore = featureTypeStore;
|
||||||
|
this.featureToggleStore = featureToggleStore;
|
||||||
|
this.featureTypes = new Map();
|
||||||
|
this.healthRatingTimer = setInterval(
|
||||||
|
() => this.setHealthRating(),
|
||||||
|
MILLISECONDS_IN_ONE_HOUR,
|
||||||
|
).unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectHealthReport(
|
||||||
|
projectId: string,
|
||||||
|
): Promise<IProjectHealthReport> {
|
||||||
|
//const overview = await this.getProjectOverview(projectId, false);
|
||||||
|
const features = await this.featureToggleStore.getFeatures();
|
||||||
|
return {
|
||||||
|
// ...overview,
|
||||||
|
potentiallyStaleCount: await this.potentiallyStaleCount(features),
|
||||||
|
activeCount: this.activeCount(features),
|
||||||
|
staleCount: this.staleCount(features),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async potentiallyStaleCount(
|
||||||
|
features: Pick<FeatureToggle, 'createdAt' | 'stale' | 'type'>[],
|
||||||
|
): Promise<number> {
|
||||||
|
const today = new Date().valueOf();
|
||||||
|
if (this.featureTypes.size === 0) {
|
||||||
|
const types = await this.featureTypeStore.getAll();
|
||||||
|
types.forEach(type => {
|
||||||
|
this.featureTypes.set(
|
||||||
|
type.name.toLowerCase(),
|
||||||
|
type.lifetimeDays,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return features.filter(feature => {
|
||||||
|
const diff = today - feature.createdAt.valueOf();
|
||||||
|
const featureTypeExpectedLifetime = this.featureTypes.get(
|
||||||
|
feature.type,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
!feature.stale &&
|
||||||
|
diff >= featureTypeExpectedLifetime * MILLISECONDS_IN_DAY
|
||||||
|
);
|
||||||
|
}).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private activeCount(features: IFeatureOverview[]): number {
|
||||||
|
return features.filter(f => !f.stale).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private staleCount(features: IFeatureOverview[]): number {
|
||||||
|
return features.filter(f => f.stale).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateHealthRating(project: IProject): Promise<number> {
|
||||||
|
const toggles = await this.featureToggleStore.getFeaturesBy({
|
||||||
|
project: project.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeToggles = toggles.filter(feature => !feature.stale);
|
||||||
|
const staleToggles = toggles.length - activeToggles.length;
|
||||||
|
const potentiallyStaleToggles = await this.potentiallyStaleCount(
|
||||||
|
activeToggles,
|
||||||
|
);
|
||||||
|
return this.getHealthRating(
|
||||||
|
toggles.length,
|
||||||
|
staleToggles,
|
||||||
|
potentiallyStaleToggles,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getHealthRating(
|
||||||
|
toggleCount: number,
|
||||||
|
staleToggleCount: number,
|
||||||
|
potentiallyStaleCount: number,
|
||||||
|
): number {
|
||||||
|
const startPercentage = 100;
|
||||||
|
const stalePercentage = (staleToggleCount / toggleCount) * 100 || 0;
|
||||||
|
const potentiallyStalePercentage =
|
||||||
|
(potentiallyStaleCount / toggleCount) * 100 || 0;
|
||||||
|
const rating = Math.round(
|
||||||
|
startPercentage - stalePercentage - potentiallyStalePercentage,
|
||||||
|
);
|
||||||
|
return rating;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setHealthRating(): Promise<void> {
|
||||||
|
const projects = await this.projectStore.getAll();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
projects.map(async project => {
|
||||||
|
const newHealth = await this.calculateHealthRating(project);
|
||||||
|
await this.projectStore.updateHealth({
|
||||||
|
id: project.id,
|
||||||
|
health: newHealth,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
clearInterval(this.healthRatingTimer);
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ import HealthService from '../services/health-service';
|
|||||||
import SettingService from '../services/setting-service';
|
import SettingService from '../services/setting-service';
|
||||||
import SessionService from '../services/session-service';
|
import SessionService from '../services/session-service';
|
||||||
import UserFeedbackService from '../services/user-feedback-service';
|
import UserFeedbackService from '../services/user-feedback-service';
|
||||||
|
import ProjectHealthService from '../services/project-health-service';
|
||||||
|
|
||||||
export interface IUnleashServices {
|
export interface IUnleashServices {
|
||||||
accessService: AccessService;
|
accessService: AccessService;
|
||||||
@ -42,4 +43,5 @@ export interface IUnleashServices {
|
|||||||
userService: UserService;
|
userService: UserService;
|
||||||
versionService: VersionService;
|
versionService: VersionService;
|
||||||
userFeedbackService: UserFeedbackService;
|
userFeedbackService: UserFeedbackService;
|
||||||
|
projectHealthService: ProjectHealthService;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user