1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-03 01:18:43 +02:00
unleash.unleash/src/lib/features/personal-dashboard/personal-dashboard-service.ts
2024-10-02 09:33:50 +02:00

179 lines
5.9 KiB
TypeScript

import type {
IProjectOwnersReadModel,
UserProjectOwner,
} from '../project/project-owners-read-model.type';
import type {
IPersonalDashboardReadModel,
PersonalFeature,
PersonalProject,
} from './personal-dashboard-read-model-type';
import type { IProjectReadModel } from '../project/project-read-model-type';
import type { IPrivateProjectChecker } from '../private-project/privateProjectCheckerType';
import type {
IAccessStore,
IAccountStore,
IEventStore,
IOnboardingReadModel,
MinimalUser,
} from '../../types';
import type { FeatureEventFormatter } from '../../addons/feature-event-formatter-md';
import { generateImageUrl } from '../../util';
import type { PersonalDashboardProjectDetailsSchema } from '../../openapi';
export class PersonalDashboardService {
private personalDashboardReadModel: IPersonalDashboardReadModel;
private projectOwnersReadModel: IProjectOwnersReadModel;
private projectReadModel: IProjectReadModel;
private privateProjectChecker: IPrivateProjectChecker;
private eventStore: IEventStore;
private featureEventFormatter: FeatureEventFormatter;
private accountStore: IAccountStore;
private onboardingReadModel: IOnboardingReadModel;
private accessStore: IAccessStore;
constructor(
personalDashboardReadModel: IPersonalDashboardReadModel,
projectOwnersReadModel: IProjectOwnersReadModel,
projectReadModel: IProjectReadModel,
onboardingReadModel: IOnboardingReadModel,
eventStore: IEventStore,
featureEventFormatter: FeatureEventFormatter,
privateProjectChecker: IPrivateProjectChecker,
accountStore: IAccountStore,
accessStore: IAccessStore,
) {
this.personalDashboardReadModel = personalDashboardReadModel;
this.projectOwnersReadModel = projectOwnersReadModel;
this.projectReadModel = projectReadModel;
this.onboardingReadModel = onboardingReadModel;
this.eventStore = eventStore;
this.featureEventFormatter = featureEventFormatter;
this.privateProjectChecker = privateProjectChecker;
this.accountStore = accountStore;
this.accessStore = accessStore;
}
getPersonalFeatures(userId: number): Promise<PersonalFeature[]> {
return this.personalDashboardReadModel.getPersonalFeatures(userId);
}
async getPersonalProjects(userId: number): Promise<PersonalProject[]> {
const [userProjectIds, userFavoritedProjectIds] = await Promise.all([
this.projectReadModel.getProjectsByUser(userId),
this.projectReadModel.getProjectsFavoritedByUser(userId),
]);
const projects = await this.projectReadModel.getProjectsForAdminUi({
ids: [...new Set([...userProjectIds, ...userFavoritedProjectIds])],
archived: false,
});
const normalizedProjects = projects.map((project) => ({
id: project.id,
name: project.name,
health: project.health,
memberCount: project.memberCount,
featureCount: project.featureCount,
}));
return normalizedProjects;
}
async getProjectOwners(userId: number): Promise<UserProjectOwner[]> {
const accessibleProjects =
await this.privateProjectChecker.getUserAccessibleProjects(userId);
const filter =
accessibleProjects.mode === 'all'
? undefined
: new Set(accessibleProjects.projects);
return this.projectOwnersReadModel.getAllUserProjectOwners(filter);
}
async getPersonalProjectDetails(
userId: number,
projectId: string,
): Promise<PersonalDashboardProjectDetailsSchema> {
const recentEvents = await this.eventStore.searchEvents(
{ limit: 4, offset: 0 },
[{ field: 'project', operator: 'IS', values: [projectId] }],
);
const onboardingStatus =
await this.onboardingReadModel.getOnboardingStatusForProject(
projectId,
);
const formattedEvents = recentEvents.map((event) => ({
summary: this.featureEventFormatter.format(event).text,
createdBy: event.createdBy,
id: event.id,
createdByImageUrl: generateImageUrl({ email: event.createdBy }),
}));
const owners =
await this.projectOwnersReadModel.getProjectOwners(projectId);
const allRoles = await this.accessStore.getAllProjectRolesForUser(
userId,
projectId,
);
const projectRoles = allRoles
.filter((role) => ['project', 'custom'].includes(role.type))
.map((role) => ({
id: role.id,
name: role.name,
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,
},
};
}
async getAdmins(): Promise<MinimalUser[]> {
return this.accountStore.getAdmins();
}
}