diff --git a/libs/common/src/constants/controller-route.ts b/libs/common/src/constants/controller-route.ts index 572bb9c..1d23d9f 100644 --- a/libs/common/src/constants/controller-route.ts +++ b/libs/common/src/constants/controller-route.ts @@ -22,6 +22,19 @@ export class ControllerRoute { public static readonly DELETE_PROJECT_DESCRIPTION = 'This endpoint deletes an existing project by its unique identifier (UUID).'; + public static readonly GET_USERS_BY_PROJECT_SUMMARY = + 'Get users by project'; + public static readonly GET_USERS_BY_PROJECT_DESCRIPTION = + 'This endpoint retrieves all users associated with a specific project.'; + public static readonly GET_USER_BY_UUID_IN_PROJECT_SUMMARY = + 'Get user by uuid in project'; + public static readonly GET_USER_BY_UUID_IN_PROJECT_DESCRIPTION = + 'This endpoint retrieves a user by their unique identifier (UUID) associated with a specific project.'; + }; + }; + static PROJECT_USER = class { + public static readonly ROUTE = '/projects/:projectUuid/user'; + static ACTIONS = class { public static readonly GET_USERS_BY_PROJECT_SUMMARY = 'Get users by project'; public static readonly GET_USERS_BY_PROJECT_DESCRIPTION = diff --git a/src/project/controllers/project-user.controller.ts b/src/project/controllers/project-user.controller.ts new file mode 100644 index 0000000..d7f5e31 --- /dev/null +++ b/src/project/controllers/project-user.controller.ts @@ -0,0 +1,48 @@ +import { ControllerRoute } from '@app/common/constants/controller-route'; +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { GetProjectParam } from '../dto'; +import { ProjectUserService } from '../services/project-user.service'; + +@ApiTags('Project Module') +@Controller({ + version: '1', + path: ControllerRoute.PROJECT_USER.ROUTE, +}) +export class ProjectUserController { + constructor(private readonly projectUserService: ProjectUserService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: + ControllerRoute.PROJECT.ACTIONS.GET_USER_BY_UUID_IN_PROJECT_SUMMARY, + description: + ControllerRoute.PROJECT.ACTIONS.GET_USER_BY_UUID_IN_PROJECT_DESCRIPTION, + }) + @Get() + async findUsersByProject( + @Param() params: GetProjectParam, + ): Promise { + return this.projectUserService.getUsersByProject(params.projectUuid); + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @ApiOperation({ + summary: ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_SUMMARY, + description: + ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_DESCRIPTION, + }) + @Get(':userUuid') + async findUserByUuidInProject( + @Param() params: GetProjectParam, + @Param('userUuid') userUuid: string, + ): Promise { + return this.projectUserService.getUserByUuidInProject( + params.projectUuid, + userUuid, + ); + } +} diff --git a/src/project/controllers/project.controller.ts b/src/project/controllers/project.controller.ts index c888acb..a39ced1 100644 --- a/src/project/controllers/project.controller.ts +++ b/src/project/controllers/project.controller.ts @@ -86,18 +86,4 @@ export class ProjectController { async findOne(@Param() params: GetProjectParam): Promise { return this.projectService.getProject(params.projectUuid); } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @ApiOperation({ - summary: ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_SUMMARY, - description: - ControllerRoute.PROJECT.ACTIONS.GET_USERS_BY_PROJECT_DESCRIPTION, - }) - @Get(':projectUuid/users') - async findUsersByProject( - @Param() params: GetProjectParam, - ): Promise { - return this.projectService.getUsersByProject(params.projectUuid); - } } diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 0afbd1c..92b12f2 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -8,18 +8,21 @@ import { SpaceRepository } from '@app/common/modules/space'; import { CommunityRepository } from '@app/common/modules/community/repositories'; import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { ProjectUserController } from './controllers/project-user.controller'; +import { ProjectUserService } from './services/project-user.service'; const CommandHandlers = [CreateOrphanSpaceHandler]; @Global() @Module({ imports: [CqrsModule], - controllers: [ProjectController], + controllers: [ProjectController, ProjectUserController], providers: [ ...CommandHandlers, SpaceRepository, CommunityRepository, ProjectService, + ProjectUserService, ProjectRepository, InviteUserRepository, UserRepository, diff --git a/src/project/services/project-user.service.ts b/src/project/services/project-user.service.ts new file mode 100644 index 0000000..aa857d0 --- /dev/null +++ b/src/project/services/project-user.service.ts @@ -0,0 +1,160 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { BaseResponseDto } from '@app/common/dto/base.response.dto'; +import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; + +import { UserStatusEnum } from '@app/common/constants/user-status.enum'; +import { RoleType } from '@app/common/constants/role.type.enum'; +import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories'; +import { UserRepository } from '@app/common/modules/user/repositories'; + +@Injectable() +export class ProjectUserService { + constructor( + private readonly inviteUserRepository: InviteUserRepository, + private readonly userRepository: UserRepository, + ) {} + + async getUsersByProject(uuid: string): Promise { + try { + // Fetch invited users + const invitedUsers = await this.inviteUserRepository.find({ + where: { project: { uuid }, isActive: true }, + select: [ + 'uuid', + 'firstName', + 'lastName', + 'email', + 'createdAt', + 'status', + 'phoneNumber', + 'jobTitle', + 'invitedBy', + 'isEnabled', + ], + relations: ['roleType'], + }); + + // Fetch project users + const users = await this.userRepository.find({ + where: { project: { uuid }, isActive: true }, + select: ['uuid', 'firstName', 'lastName', 'email', 'createdAt'], + relations: ['roleType'], + }); + + // Combine both arrays + const allUsers = [...users, ...invitedUsers]; + + const normalizedUsers = allUsers.map((user) => { + const createdAt = new Date(user.createdAt); + const createdDate = createdAt.toLocaleDateString(); + const createdTime = createdAt.toLocaleTimeString(); + + // Normalize user properties + const normalizedProps = this.normalizeUserProperties(user); + + // Return the normalized user object + return { + ...user, + createdDate, + createdTime, + ...normalizedProps, + }; + }); + + return new SuccessResponseDto({ + message: `Users in project with ID ${uuid} retrieved successfully`, + data: normalizedUsers, + statusCode: HttpStatus.OK, + }); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `An error occurred while retrieving users in the project with id ${uuid}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + async getUserByUuidInProject( + projectUuid: string, + userUuid: string, + ): Promise { + try { + let user; + user = await this.inviteUserRepository.findOne({ + where: { + project: { uuid: projectUuid }, + uuid: userUuid, + isActive: true, + }, + select: [ + 'uuid', + 'firstName', + 'lastName', + 'email', + 'createdAt', + 'status', + 'phoneNumber', + 'jobTitle', + 'invitedBy', + 'isEnabled', + ], + relations: ['roleType', 'spaces'], + }); + if (!user) { + user = await this.userRepository.findOne({ + where: { + project: { uuid: projectUuid }, + uuid: userUuid, + isActive: true, + }, + select: ['uuid', 'firstName', 'lastName', 'email', 'createdAt'], + relations: ['roleType', 'userSpaces'], + }); + } + if (!user) { + throw new HttpException( + `User with ID ${userUuid} not found in project ${projectUuid}`, + HttpStatus.NOT_FOUND, + ); + } + const responseData = this.formatUserResponse(user); + return new SuccessResponseDto({ + message: `User in project with ID ${projectUuid} retrieved successfully`, + data: responseData, + statusCode: HttpStatus.OK, + }); + } catch (error) { + if (error instanceof HttpException) { + throw error; + } + + throw new HttpException( + `An error occurred while retrieving user in the project with id ${projectUuid}`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + private formatUserResponse(user: any) { + const createdAt = new Date(user.createdAt); + return { + ...user, + createdDate: createdAt.toLocaleDateString(), + createdTime: createdAt.toLocaleTimeString(), + ...this.normalizeUserProperties(user), + }; + } + private normalizeUserProperties(user: any) { + return { + status: user.status ?? UserStatusEnum.ACTIVE, + invitedBy: user.invitedBy ?? RoleType.SPACE_MEMBER, + isEnabled: user.isEnabled ?? true, + phoneNumber: user.phoneNumber ?? null, + jobTitle: user.jobTitle ?? null, + roleType: user.roleType?.type ?? null, + spaces: user.spaces ?? user.userSpaces, + }; + } +} diff --git a/src/project/services/project.service.ts b/src/project/services/project.service.ts index 1d5e609..75aa67a 100644 --- a/src/project/services/project.service.ts +++ b/src/project/services/project.service.ts @@ -12,17 +12,11 @@ import { ProjectDto } from '@app/common/modules/project/dtos'; import { PageResponse } from '@app/common/dto/pagination.response.dto'; import { CommandBus } from '@nestjs/cqrs'; import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command'; -import { InviteUserRepository } from '@app/common/modules/Invite-user/repositiories'; -import { UserRepository } from '@app/common/modules/user/repositories'; -import { UserStatusEnum } from '@app/common/constants/user-status.enum'; -import { RoleType } from '@app/common/constants/role.type.enum'; @Injectable() export class ProjectService { constructor( private readonly projectRepository: ProjectRepository, - private readonly inviteUserRepository: InviteUserRepository, - private readonly userRepository: UserRepository, private commandBus: CommandBus, ) {} @@ -187,78 +181,6 @@ export class ProjectService { } } - async getUsersByProject(uuid: string): Promise { - try { - // Fetch invited users - const invitedUsers = await this.inviteUserRepository.find({ - where: { project: { uuid }, isActive: true }, - select: [ - 'firstName', - 'lastName', - 'email', - 'createdAt', - 'status', - 'phoneNumber', - 'jobTitle', - 'invitedBy', - 'isEnabled', - ], - relations: ['roleType'], - }); - - // Fetch project users - const users = await this.userRepository.find({ - where: { project: { uuid }, isActive: true }, - select: ['firstName', 'lastName', 'email', 'createdAt'], - relations: ['roleType'], - }); - - // Combine both arrays - const allUsers = [...users, ...invitedUsers]; - - const normalizedUsers = allUsers.map((user) => { - const createdAt = new Date(user.createdAt); - const createdDate = createdAt.toLocaleDateString(); - const createdTime = createdAt.toLocaleTimeString(); - - // Normalize user properties - const normalizedProps = this.normalizeUserProperties(user); - - // Return the normalized user object - return { - ...user, - createdDate, - createdTime, - ...normalizedProps, - }; - }); - - return new SuccessResponseDto({ - message: `Users in project with ID ${uuid} retrieved successfully`, - data: normalizedUsers, - statusCode: HttpStatus.OK, - }); - } catch (error) { - if (error instanceof HttpException) { - throw error; - } - - throw new HttpException( - `An error occurred while retrieving users in the project with id ${uuid}`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } - normalizeUserProperties(user: any) { - return { - status: user.status ?? UserStatusEnum.ACTIVE, - invitedBy: user.invitedBy ?? RoleType.SPACE_MEMBER, - isEnabled: user.isEnabled ?? true, - phoneNumber: user.phoneNumber ?? null, - jobTitle: user.jobTitle ?? null, - roleType: user.roleType?.type ?? null, - }; - } async findOne(uuid: string): Promise { const project = await this.projectRepository.findOne({ where: { uuid } }); return project;