Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update team members endpoint #666

Merged
merged 4 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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)[];
Expand All @@ -18,9 +18,11 @@ export interface BaseInterfaceRepository<T> {

create(item: T): Promise<T>;

insertMany(listOfItems: T[]): Promise<T[]>;

update(id: string, item: T): Promise<T>;

deleteMany(field: ModelProps<T>, withSession: boolean): Promise<number>;
deleteMany(field: FilterQuery<T>, withSession: boolean): Promise<number>;

countDocuments(): Promise<number>;

Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -47,6 +47,10 @@ export class MongoGenericRepository<T> implements BaseInterfaceRepository<T> {
return this._repository.create(item);
}

insertMany(listOfItems: T[]): Promise<T[]> {
return this._repository.insertMany(listOfItems);
}

update(id: string, item: T): Promise<T> {
return this._repository.findByIdAndUpdate(id, item).exec();
}
Expand Down Expand Up @@ -74,7 +78,7 @@ export class MongoGenericRepository<T> implements BaseInterfaceRepository<T> {
.exec();
}

async deleteMany(field: ModelProps<T>, withSession = false): Promise<number> {
async deleteMany(field: FilterQuery<T>, withSession = false): Promise<number> {
const { deletedCount } = await this._repository
.deleteMany(field, { session: withSession ? this._session : undefined })
.exec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
35 changes: 35 additions & 0 deletions backend/src/modules/teams/controller/team.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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!' })
Expand Down
13 changes: 13 additions & 0 deletions backend/src/modules/teams/dto/update.team.user.dto.ts
Original file line number Diff line number Diff line change
@@ -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[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ import { TeamUserDocument } from '../../entities/team.user.schema';

export interface UpdateTeamApplicationInterface {
updateTeamUser(teamData: TeamUserDto): Promise<LeanDocument<TeamUserDocument> | null>;
addAndRemoveTeamUsers(
addUsers: TeamUserDto[],
removeUsers: string[]
): Promise<LeanDocument<TeamUserDocument[]>>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ import { TeamUserDocument } from '../../entities/team.user.schema';

export interface UpdateTeamServiceInterface {
updateTeamUser(teamData: TeamUserDto): Promise<LeanDocument<TeamUserDocument> | null>;
addAndRemoveTeamUsers(
addUsers: TeamUserDto[],
removeUsers: string[]
): Promise<LeanDocument<TeamUserDocument[]>>;
}
36 changes: 35 additions & 1 deletion backend/src/modules/teams/services/update.team.service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -15,4 +16,37 @@ export default class UpdateTeamService implements UpdateTeamServiceInterface {
updateTeamUser(teamData: TeamUserDto): Promise<TeamUser | null> {
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);
}
}