refactor me service

This commit is contained in:
Alex Holliday committed 2026-01-07 22:18:48 +00:00
1 parent 03ce8f84ea
commit c1bb1fd29d
9 files changed
+91 -40

No files matched your search

@@ -4,12 +4,12 @@ import { useNavigate } from "react-router";
import { usePost, useGet } from "@/hooks/UseApi";
import type { ApiResponse } from "@/types/api";
import type { IMonitor } from "@/types/monitor";
import type { IMaintenance } from "@/types/maintenance";
import type { Maintenance } from "@/types/maintenance";
import type { FormValues } from "@/pages/maintenance/MaintenanceForm";
const MaintenanceCreatePage = () => {
const navigate = useNavigate();
const { post, loading } = usePost<FormValues, IMaintenance>();
const { post, loading } = usePost<FormValues, Maintenance>();
const { response, loading: monitorsLoading } =
useGet<ApiResponse<IMonitor[]>>("/monitors");
+9 -1
View File
@@ -125,7 +125,15 @@ export const initServices = async () => {
teamRepository,
teamMembershipRepository
);
const meService = new MeService(entitlementsProvider);
const meService = new MeService(
entitlementsProvider,
userRepository,
orgRepository,
orgMembershipRepository,
teamRepository,
teamMembershipRepository,
roleRepository
);
const monitorService = new MonitorService(
jobQueue,
monitorRepository,
+4 -1
View File
@@ -31,8 +31,11 @@ export { default as MongoTeamMembershipRepository } from "@/repositories/team-me
export type { IOrgRepository } from "@/repositories/org/IOrgRepository.js";
export { default as MongoOrgRepository } from "@/repositories/org/MongoOrgRepository.js";
export type { IOrgMembershipRepository } from "@/repositories/org-membership/IOrgMembershopRepository.js";
export type { IOrgMembershipRepository } from "@/repositories/org-membership/IOrgMembershipRepository.js";
export { default as MongoOrgMembershipRepository } from "@/repositories/org-membership/MongoOrgMembershipRepository.js";
export type { IMaintenanceRepository } from "@/repositories/maintenance/IMaintenanceRepository.js";
export { default as MongoMaintenanceRepository } from "@/repositories/maintenance/MongoMaintenanceRepository.js";
export type { INotificationChannelRepository } from "@/repositories/notification-channels/INotificationChannelRepository.js";
export { default as MongoNotificationChannelRepository } from "@/repositories/notification-channels/MongoNotificationChannelRepository.js";
@@ -0,0 +1,7 @@
export interface INotificationChannelRepository {
// create
// single fetch
// collection fetch
// update
// delete
}
@@ -0,0 +1,5 @@
import { INotificationChannelRepository } from "@/repositories/index.js";
class MongoNotificationRepository implements INotificationChannelRepository {}
export default MongoNotificationRepository;
@@ -4,6 +4,7 @@ export interface IUserRepository {
create(userData: Partial<User>): Promise<User>;
// single fetch
findByEmail: (email: string) => Promise<User | null>;
findByUserId: (userId: string) => Promise<User | null>;
// collection fetch
// update
updateById: (
@@ -28,6 +28,12 @@ class MongoUserRepository implements IUserRepository {
return this.toEntity(user);
};
findByUserId = async (userId: string) => {
const user = await User.findById(userId);
if (!user) return null;
return this.toEntity(user);
};
updateById = async (userId: string, updateData: Partial<UserEntity>) => {
const result = await User.findOneAndUpdate(
{ _id: userId },
+57 -36
View File
@@ -1,16 +1,15 @@
import {
User,
Org,
OrgMembership,
Team,
TeamMembership,
Role,
IRole,
} from "@/db/models/index.js";
import ApiError from "@/utils/ApiError.js";
import type { IEntitlementsProvider } from "@/services/system/EntitlementsService.js";
import type { Entitlements } from "@/types/entitlements.js";
import type { UserReturnable } from "@/types/domain/index.js";
import type {
IUserRepository,
IOrgRepository,
IOrgMembershipRepository,
ITeamRepository,
ITeamMembershipRepository,
IRoleRepository,
} from "@/repositories/index.js";
export interface IMeService {
me(userId: string): Promise<UserReturnable>;
@@ -26,73 +25,95 @@ const SERVICE_NAME = "MeService";
class MeService implements IMeService {
public SERVICE_NAME = SERVICE_NAME;
private entitlementsProvider: IEntitlementsProvider;
private userRepository: IUserRepository;
private orgRepository: IOrgRepository;
private orgMembershipRepository: IOrgMembershipRepository;
private teamRepository: ITeamRepository;
private teamMembershipRepository: ITeamMembershipRepository;
private roleRepository: IRoleRepository;
constructor(entitlementsProvider: IEntitlementsProvider) {
constructor(
entitlementsProvider: IEntitlementsProvider,
userRepository: IUserRepository,
orgRepository: IOrgRepository,
orgMembershipRepository: IOrgMembershipRepository,
teamRepository: ITeamRepository,
teamMembershipRepository: ITeamMembershipRepository,
roleRepository: IRoleRepository
) {
this.entitlementsProvider = entitlementsProvider;
this.userRepository = userRepository;
this.orgRepository = orgRepository;
this.orgMembershipRepository = orgMembershipRepository;
this.teamRepository = teamRepository;
this.teamMembershipRepository = teamMembershipRepository;
this.roleRepository = roleRepository;
}
me = async (userId: string): Promise<UserReturnable> => {
// Need to get teamIds
const user = await User.findById(userId);
const user = await this.userRepository.findByUserId(userId);
if (!user) {
throw new ApiError("User not found", 404);
}
// Find OrgMembership
const orgMembership = await OrgMembership.findOne({
userId: user._id,
}).lean();
const orgMembership = await this.orgMembershipRepository.findByUserId(
user.id
);
if (!orgMembership) {
throw new ApiError("User is not part of any organization");
}
// Find org and roles
const org = await Org.findById(orgMembership.orgId).lean();
const org = await this.orgRepository.findById(orgMembership.orgId);
if (!org) {
throw new ApiError("Organization not found");
}
const orgRoles = await Role.findById(orgMembership.roleId).lean();
// Get teams
const teamMembershipWithRoles = await TeamMembership.find({
userId: user._id,
})
.populate<{ roleId: IRole }>("roleId")
.lean();
const teamIds = teamMembershipWithRoles.map((tm) => tm.teamId.toString());
const teams = await Team.find({ _id: { $in: teamIds } }).select(
"_id, name"
const orgRole = await this.roleRepository.findById(
orgMembership.roleId || "",
org.id
);
const teamMap = new Map(teams.map((t) => [t._id.toString(), t]));
const returnableTeams = teamMembershipWithRoles.map((tm) => {
const team = teamMap.get(tm.teamId.toString());
// Get teams
const teamMembershipsWithRole =
await this.teamMembershipRepository.findByUserIdWithRole(user.id);
if (!teamMembershipsWithRole) {
throw new ApiError("Team memberships not found for user");
}
const teamIds = teamMembershipsWithRole.map((tm) => tm.teamId);
const teams = await this.teamRepository.findManyById(teamIds);
const teamMap = new Map(teams.map((t) => [t.id, t]));
const returnableTeams = teamMembershipsWithRole.map((tm) => {
const team = teamMap.get(tm.teamId);
if (!team) {
throw new ApiError("Team not found for membership");
}
return {
id: team._id.toString(),
id: team.id,
name: team.name,
permissions: tm.roleId?.permissions ?? [],
permissions: tm.role?.permissions ?? [],
};
});
const entitlements: Entitlements =
await this.entitlementsProvider.getForOrg(org._id.toString());
await this.entitlementsProvider.getForOrg(org.id);
const returnableUser: UserReturnable = {
id: user._id.toString(),
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
org: {
name: org.name,
planKey: org.planKey,
permissions: orgRoles?.permissions || [],
permissions: orgRole?.permissions || [],
},
teams: returnableTeams,
entitlements,