diff --git a/backend/src/libs/repositories/interfaces/base.repository.interface.ts b/backend/src/libs/repositories/interfaces/base.repository.interface.ts index 43ebe19fb..f59d38e78 100644 --- a/backend/src/libs/repositories/interfaces/base.repository.interface.ts +++ b/backend/src/libs/repositories/interfaces/base.repository.interface.ts @@ -1,4 +1,4 @@ -import { PopulateOptions, UpdateQuery } from 'mongoose'; +import { FilterQuery, PopulateOptions, UpdateQuery } from 'mongoose'; import { ModelProps, SelectedValues } from '../types'; export type PopulateType = PopulateOptions | (PopulateOptions | string)[]; @@ -18,9 +18,11 @@ export interface BaseInterfaceRepository { create(item: T): Promise; + insertMany(listOfItems: T[]): Promise; + update(id: string, item: T): Promise; - deleteMany(field: ModelProps, withSession: boolean): Promise; + deleteMany(field: FilterQuery, withSession: boolean): Promise; countDocuments(): Promise; diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 26a2f0bcc..d7e52ea7d 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -1,4 +1,4 @@ -import { ClientSession, Model, UpdateQuery } from 'mongoose'; +import { ClientSession, FilterQuery, Model, UpdateQuery } from 'mongoose'; import { BaseInterfaceRepository, PopulateType } from '../interfaces/base.repository.interface'; import { ModelProps, SelectedValues } from '../types'; @@ -47,6 +47,10 @@ export class MongoGenericRepository implements BaseInterfaceRepository { return this._repository.create(item); } + insertMany(listOfItems: T[]): Promise { + return this._repository.insertMany(listOfItems); + } + update(id: string, item: T): Promise { return this._repository.findByIdAndUpdate(id, item).exec(); } @@ -74,7 +78,7 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .exec(); } - async deleteMany(field: ModelProps, withSession = false): Promise { + async deleteMany(field: FilterQuery, withSession = false): Promise { const { deletedCount } = await this._repository .deleteMany(field, { session: withSession ? this._session : undefined }) .exec(); diff --git a/backend/src/modules/teams/applications/update.team.application.ts b/backend/src/modules/teams/applications/update.team.application.ts index 305da87c8..0e59722ef 100644 --- a/backend/src/modules/teams/applications/update.team.application.ts +++ b/backend/src/modules/teams/applications/update.team.application.ts @@ -14,4 +14,7 @@ export class UpdateTeamApplication implements UpdateTeamApplicationInterface { updateTeamUser(teamData: TeamUserDto) { return this.updateTeamService.updateTeamUser(teamData); } + addAndRemoveTeamUsers(addUsers: TeamUserDto[], removeUsers: string[]) { + return this.updateTeamService.addAndRemoveTeamUsers(addUsers, removeUsers); + } } diff --git a/backend/src/modules/teams/controller/team.controller.ts b/backend/src/modules/teams/controller/team.controller.ts index a2e405af7..9b1cf0f1a 100644 --- a/backend/src/modules/teams/controller/team.controller.ts +++ b/backend/src/modules/teams/controller/team.controller.ts @@ -47,6 +47,7 @@ import { UpdateTeamApplication } from '../applications/update.team.application'; import { CreateTeamDto } from '../dto/crate-team.dto'; import TeamDto from '../dto/team.dto'; import TeamUserDto from '../dto/team.user.dto'; +import UpdateTeamUserDto from '../dto/update.team.user.dto'; import { CreateTeamApplicationInterface } from '../interfaces/applications/create.team.application.interface'; import { GetTeamApplicationInterface } from '../interfaces/applications/get.team.application.interface'; import { TYPES } from '../interfaces/types'; @@ -225,6 +226,40 @@ export default class TeamsController { return teamUser; } + @ApiOperation({ summary: 'Add and remove team members' }) + @ApiParam({ type: String, name: 'teamId', required: true }) + @ApiBody({ type: UpdateTeamUserDto }) + @ApiOkResponse({ + type: TeamUserDto, + description: 'Team member updated successfully!' + }) + @ApiBadRequestResponse({ + description: 'Bad Request', + type: BadRequestResponse + }) + @ApiUnauthorizedResponse({ + description: 'Unauthorized', + type: UnauthorizedResponse + }) + @ApiNotFoundResponse({ + type: NotFoundResponse, + description: 'Not found!' + }) + @ApiForbiddenResponse({ + description: 'Forbidden', + type: ForbiddenResponse + }) + @ApiInternalServerErrorResponse({ + description: 'Internal Server Error', + type: InternalServerErrorResponse + }) + @TeamUser([TeamRoles.ADMIN, TeamRoles.STAKEHOLDER]) + @UseGuards(TeamUserGuard) + @Put('/:teamId/addAndRemove') + addAndRemoveTeamUsers(@Body() users: UpdateTeamUserDto) { + return this.updateTeamApp.addAndRemoveTeamUsers(users.addUsers, users.removeUsers); + } + @ApiOperation({ summary: 'Delete a specific team' }) @ApiParam({ type: String, name: 'teamId', required: true }) @ApiOkResponse({ type: Boolean, description: 'Team successfully deleted!' }) diff --git a/backend/src/modules/teams/dto/update.team.user.dto.ts b/backend/src/modules/teams/dto/update.team.user.dto.ts new file mode 100644 index 000000000..95560b75b --- /dev/null +++ b/backend/src/modules/teams/dto/update.team.user.dto.ts @@ -0,0 +1,13 @@ +import TeamUserDto from 'src/modules/teams/dto/team.user.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray } from 'class-validator'; + +export default class UpdateTeamUserDto { + @ApiProperty({ description: 'List of users to add on team' }) + @IsArray() + addUsers!: TeamUserDto[]; + + @ApiProperty({ description: 'List of users ids to remove from team' }) + @IsArray() + removeUsers!: string[]; +} diff --git a/backend/src/modules/teams/interfaces/applications/update.team.application.interface.ts b/backend/src/modules/teams/interfaces/applications/update.team.application.interface.ts index 7b6bd6c61..2d5a3c9af 100644 --- a/backend/src/modules/teams/interfaces/applications/update.team.application.interface.ts +++ b/backend/src/modules/teams/interfaces/applications/update.team.application.interface.ts @@ -4,4 +4,8 @@ import { TeamUserDocument } from '../../entities/team.user.schema'; export interface UpdateTeamApplicationInterface { updateTeamUser(teamData: TeamUserDto): Promise | null>; + addAndRemoveTeamUsers( + addUsers: TeamUserDto[], + removeUsers: string[] + ): Promise>; } diff --git a/backend/src/modules/teams/interfaces/services/update.team.service.interface.ts b/backend/src/modules/teams/interfaces/services/update.team.service.interface.ts index b6330823b..3fde8ea5d 100644 --- a/backend/src/modules/teams/interfaces/services/update.team.service.interface.ts +++ b/backend/src/modules/teams/interfaces/services/update.team.service.interface.ts @@ -4,4 +4,8 @@ import { TeamUserDocument } from '../../entities/team.user.schema'; export interface UpdateTeamServiceInterface { updateTeamUser(teamData: TeamUserDto): Promise | null>; + addAndRemoveTeamUsers( + addUsers: TeamUserDto[], + removeUsers: string[] + ): Promise>; } diff --git a/backend/src/modules/teams/services/update.team.service.ts b/backend/src/modules/teams/services/update.team.service.ts index ee951d5f3..2749c9fda 100644 --- a/backend/src/modules/teams/services/update.team.service.ts +++ b/backend/src/modules/teams/services/update.team.service.ts @@ -1,4 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { BadRequestException, Inject, Injectable } from '@nestjs/common'; +import { DELETE_FAILED, INSERT_FAILED, UPDATE_FAILED } from 'src/libs/exceptions/messages'; import TeamUserDto from '../dto/team.user.dto'; import TeamUser from '../entities/team.user.schema'; import { UpdateTeamServiceInterface } from '../interfaces/services/update.team.service.interface'; @@ -15,4 +16,37 @@ export default class UpdateTeamService implements UpdateTeamServiceInterface { updateTeamUser(teamData: TeamUserDto): Promise { return this.teamUserRepository.updateTeamUser(teamData); } + + async addAndRemoveTeamUsers(addUsers: TeamUserDto[], removeUsers: string[]) { + try { + let createdTeamUsers: TeamUser[] = []; + + if (addUsers.length > 0) createdTeamUsers = await this.addTeamUsers(addUsers); + + if (removeUsers.length > 0) await this.deleteTeamUsers(removeUsers, true); + + return createdTeamUsers; + } catch (error) { + throw new BadRequestException(UPDATE_FAILED); + } + } + + async addTeamUsers(teamUsers: TeamUserDto[]) { + const createdTeamUsers = await this.teamUserRepository.insertMany(teamUsers); + + if (createdTeamUsers.length < 1) throw new Error(INSERT_FAILED); + + return createdTeamUsers; + } + + async deleteTeamUsers(teamUsers: string[], withSession: boolean) { + const deletedCount = await this.teamUserRepository.deleteMany( + { + _id: { $in: teamUsers } + }, + withSession + ); + + if (deletedCount <= 0) throw new Error(DELETE_FAILED); + } }