From e65881ba4efd285f2caf73fae9ec370d89f6ef38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Thu, 24 Nov 2022 17:56:22 +0000 Subject: [PATCH 1/8] feat: added delete team endpoint --- .../applications/delete.team.application.ts | 16 ++++ .../teams/controller/team.controller.ts | 37 ++++++++- .../delete.team.application.interface.ts | 3 + .../services/delete.team.service.interface.ts | 4 + backend/src/modules/teams/interfaces/types.ts | 8 +- backend/src/modules/teams/providers.ts | 12 +++ .../teams/services/delete.team.service.ts | 82 +++++++++++++++++++ backend/src/modules/teams/teams.module.ts | 6 +- 8 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 backend/src/modules/teams/applications/delete.team.application.ts create mode 100644 backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts create mode 100644 backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts create mode 100644 backend/src/modules/teams/services/delete.team.service.ts diff --git a/backend/src/modules/teams/applications/delete.team.application.ts b/backend/src/modules/teams/applications/delete.team.application.ts new file mode 100644 index 000000000..006ef4fde --- /dev/null +++ b/backend/src/modules/teams/applications/delete.team.application.ts @@ -0,0 +1,16 @@ +import { Inject, Injectable } from '@nestjs/common'; + +import { DeleteTeamApplicationInterface } from '../interfaces/applications/delete.team.application.interface'; +import { TYPES } from '../interfaces/types'; + +@Injectable() +export class DeleteTeamApplication implements DeleteTeamApplicationInterface { + constructor( + @Inject(TYPES.services.DeleteTeamService) + private deleteTeamServices: DeleteTeamApplicationInterface + ) {} + + delete(teamId: string) { + return this.deleteTeamServices.delete(teamId); + } +} diff --git a/backend/src/modules/teams/controller/team.controller.ts b/backend/src/modules/teams/controller/team.controller.ts index 6f8b547c0..c061d078c 100644 --- a/backend/src/modules/teams/controller/team.controller.ts +++ b/backend/src/modules/teams/controller/team.controller.ts @@ -2,6 +2,7 @@ import { BadRequestException, Body, Controller, + Delete, Get, Inject, Param, @@ -33,7 +34,7 @@ import { import { TeamParams } from 'libs/dto/param/team.params'; import { TeamQueryParams } from 'libs/dto/param/team.query.params'; import { TeamRoles } from 'libs/enum/team.roles'; -import { INSERT_FAILED, UPDATE_FAILED } from 'libs/exceptions/messages'; +import { DELETE_FAILED, INSERT_FAILED, UPDATE_FAILED } from 'libs/exceptions/messages'; import JwtAuthenticationGuard from 'libs/guards/jwtAuth.guard'; import RequestWithUser from 'libs/interfaces/requestWithUser.interface'; import { BadRequestResponse } from 'libs/swagger/errors/bad-request.swagger'; @@ -43,6 +44,7 @@ import { UnauthorizedResponse } from 'libs/swagger/errors/unauthorized.swagger'; import { TeamUserGuard } from '../../../libs/guards/teamRoles.guard'; import { ForbiddenResponse } from '../../../libs/swagger/errors/forbidden.swagger'; import { NotFoundResponse } from '../../../libs/swagger/errors/not-found.swagger'; +import { DeleteTeamApplication } from '../applications/delete.team.application'; import { UpdateTeamApplication } from '../applications/update.team.application'; import { CreateTeamDto } from '../dto/crate-team.dto'; import TeamDto from '../dto/team.dto'; @@ -64,7 +66,9 @@ export default class TeamsController { @Inject(TYPES.applications.GetTeamApplication) private getTeamApp: GetTeamApplicationInterface, @Inject(TYPES.applications.UpdateTeamApplication) - private updateTeamApp: UpdateTeamApplication + private updateTeamApp: UpdateTeamApplication, + @Inject(TYPES.applications.DeleteTeamApplication) + private deleteTeamApp: DeleteTeamApplication ) {} @ApiOperation({ summary: 'Create a new team' }) @@ -220,4 +224,33 @@ export default class TeamsController { return teamUser; } + + @ApiOperation({ summary: 'Delete a specific team' }) + @ApiParam({ type: String, name: 'teamId', required: true }) + @ApiOkResponse({ type: Boolean, description: 'Team successfully deleted!' }) + @ApiBadRequestResponse({ + description: 'Bad Request', + type: BadRequestResponse + }) + @ApiUnauthorizedResponse({ + description: 'Unauthorized', + type: UnauthorizedResponse + }) + @ApiInternalServerErrorResponse({ + description: 'Internal Server Error', + type: InternalServerErrorResponse + }) + @ApiForbiddenResponse({ + description: 'Forbidden', + type: ForbiddenResponse + }) + @TeamUser(TeamRoles.ADMIN) + @UseGuards(TeamUserGuard) + @Delete(':teamId') + async deleteTeam(@Param() { teamId }: TeamParams) { + const result = await this.deleteTeamApp.delete(teamId); + if (!result) throw new BadRequestException(DELETE_FAILED); + + return result; + } } diff --git a/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts b/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts new file mode 100644 index 000000000..651ffa40c --- /dev/null +++ b/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts @@ -0,0 +1,3 @@ +export interface DeleteTeamApplicationInterface { + delete(teamId: string): Promise; +} diff --git a/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts b/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts new file mode 100644 index 000000000..e13d4ae87 --- /dev/null +++ b/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts @@ -0,0 +1,4 @@ +export interface DeleteTeamServiceInterface { + // delete doesn't return an object + delete(teamId: string, userId: string): Promise; +} diff --git a/backend/src/modules/teams/interfaces/types.ts b/backend/src/modules/teams/interfaces/types.ts index 2e8f45e71..889d462ca 100644 --- a/backend/src/modules/teams/interfaces/types.ts +++ b/backend/src/modules/teams/interfaces/types.ts @@ -2,14 +2,14 @@ export const TYPES = { services: { CreateTeamService: 'CreateTeamService', GetTeamService: 'GetTeamService', - UpdateTeamService: 'UpdateTeamService' - // DeleteTeamService: 'DeleteTeamService', + UpdateTeamService: 'UpdateTeamService', + DeleteTeamService: 'DeleteTeamService' }, applications: { CreateTeamApplication: 'CreateTeamApplication', GetTeamApplication: 'GetTeamApplication', - UpdateTeamApplication: 'UpdateTeamApplication' - // DeleteBoardApplication: 'DeleteBoardApplication', + UpdateTeamApplication: 'UpdateTeamApplication', + DeleteTeamApplication: 'DeleteTeamApplication' // UpdateBoardApplication: 'UpdateBoardApplication', } }; diff --git a/backend/src/modules/teams/providers.ts b/backend/src/modules/teams/providers.ts index a448fbb14..d2cad2429 100644 --- a/backend/src/modules/teams/providers.ts +++ b/backend/src/modules/teams/providers.ts @@ -1,8 +1,10 @@ import { CreateTeamApplication } from './applications/create.team.application'; +import { DeleteTeamApplication } from './applications/delete.team.application'; import { GetTeamApplication } from './applications/get.team.application'; import { UpdateTeamApplication } from './applications/update.team.application'; import { TYPES } from './interfaces/types'; import CreateTeamService from './services/create.team.service'; +import DeleteTeamService from './services/delete.team.service'; import GetTeamService from './services/get.team.service'; import UpdateTeamService from './services/update.team.service'; @@ -35,3 +37,13 @@ export const updateTeamApplication = { provide: TYPES.applications.UpdateTeamApplication, useClass: UpdateTeamApplication }; + +export const deleteTeamService = { + provide: TYPES.services.DeleteTeamService, + useClass: DeleteTeamService +}; + +export const deleteTeamApplication = { + provide: TYPES.applications.DeleteTeamApplication, + useClass: DeleteTeamApplication +}; diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts new file mode 100644 index 000000000..6741b0842 --- /dev/null +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -0,0 +1,82 @@ +import { forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { ClientSession, LeanDocument, Model } from 'mongoose'; + +import { DELETE_FAILED } from 'libs/exceptions/messages'; + +import { DeleteTeamServiceInterface } from '../interfaces/services/delete.team.service.interface'; +import { GetTeamServiceInterface } from '../interfaces/services/get.team.service.interface'; +import * as Teams from '../interfaces/types'; +import Team, { TeamDocument } from '../schemas/teams.schema'; + +@Injectable() +export default class DeleteTeamService implements DeleteTeamServiceInterface { + constructor( + @InjectModel(Team.name) private teamModel: Model, + @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) + private getTeamService: GetTeamServiceInterface + ) {} + + async delete(teamId: string): Promise { + const team = await this.getTeam(teamId); + // no need ? + if (!team) { + throw new NotFoundException('Team not found!'); + } + + const teamSession = await this.teamModel.db.startSession(); + teamSession.startTransaction(); + try { + // delete team + await this.deleteTeam(teamId, teamSession); + await teamSession.commitTransaction(); + return true; + } catch (e) { + await teamSession.abortTransaction(); + } finally { + await teamSession.endSession(); + } + + return false; + } + + /* ----- HELPERS ------ */ + private async getTeam(teamId: string): Promise> { + const team = await this.getTeamService.getTeam(teamId); + if (!team) { + throw new NotFoundException('Team not found!'); + } + return team; + } + + // private async getUser(userId: string): Promise> { + // const user = await this.getUserService.getById(userId); + // if (!user) { + // throw new NotFoundException('User not found!'); + // } + // return user; + // } + + // private async getTeamUser( + // userId: string, + // teamId: string + // ): Promise> { + // const teamUser = await this.getTeamService.getTeamUser(userId, teamId); + // if (!teamUser) { + // throw new NotFoundException('User does not belong to team!'); + // } + // return teamUser; + // } + + private async deleteTeam(teamId: string, teamSession: ClientSession) { + const result = await this.teamModel.findOneAndRemove( + { + _id: teamId + }, + { session: teamSession } + ); + + if (!result) throw Error(DELETE_FAILED); + return { _id: result._id }; + } +} diff --git a/backend/src/modules/teams/teams.module.ts b/backend/src/modules/teams/teams.module.ts index 427d93a9f..a75280685 100644 --- a/backend/src/modules/teams/teams.module.ts +++ b/backend/src/modules/teams/teams.module.ts @@ -10,6 +10,8 @@ import TeamsController from './controller/team.controller'; import { createTeamApplication, createTeamService, + deleteTeamApplication, + deleteTeamService, getTeamApplication, getTeamService, updateTeamApplication, @@ -24,7 +26,9 @@ import { getTeamService, getTeamApplication, updateTeamService, - updateTeamApplication + updateTeamApplication, + deleteTeamApplication, + deleteTeamService ], controllers: [TeamsController], exports: [getTeamApplication, getTeamService, createTeamService, updateTeamService] From 08c5afde885858a18f850d6be035a80e3015f1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 25 Nov 2022 10:04:39 +0000 Subject: [PATCH 2/8] fix: removed unnecessary throws --- .../teams/services/delete.team.service.ts | 49 ++----------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts index 6741b0842..ffc60113b 100644 --- a/backend/src/modules/teams/services/delete.team.service.ts +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -1,33 +1,20 @@ -import { forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { ClientSession, LeanDocument, Model } from 'mongoose'; +import { ClientSession, Model } from 'mongoose'; import { DELETE_FAILED } from 'libs/exceptions/messages'; import { DeleteTeamServiceInterface } from '../interfaces/services/delete.team.service.interface'; -import { GetTeamServiceInterface } from '../interfaces/services/get.team.service.interface'; -import * as Teams from '../interfaces/types'; import Team, { TeamDocument } from '../schemas/teams.schema'; @Injectable() export default class DeleteTeamService implements DeleteTeamServiceInterface { - constructor( - @InjectModel(Team.name) private teamModel: Model, - @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) - private getTeamService: GetTeamServiceInterface - ) {} + constructor(@InjectModel(Team.name) private teamModel: Model) {} async delete(teamId: string): Promise { - const team = await this.getTeam(teamId); - // no need ? - if (!team) { - throw new NotFoundException('Team not found!'); - } - const teamSession = await this.teamModel.db.startSession(); teamSession.startTransaction(); try { - // delete team await this.deleteTeam(teamId, teamSession); await teamSession.commitTransaction(); return true; @@ -40,34 +27,6 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { return false; } - /* ----- HELPERS ------ */ - private async getTeam(teamId: string): Promise> { - const team = await this.getTeamService.getTeam(teamId); - if (!team) { - throw new NotFoundException('Team not found!'); - } - return team; - } - - // private async getUser(userId: string): Promise> { - // const user = await this.getUserService.getById(userId); - // if (!user) { - // throw new NotFoundException('User not found!'); - // } - // return user; - // } - - // private async getTeamUser( - // userId: string, - // teamId: string - // ): Promise> { - // const teamUser = await this.getTeamService.getTeamUser(userId, teamId); - // if (!teamUser) { - // throw new NotFoundException('User does not belong to team!'); - // } - // return teamUser; - // } - private async deleteTeam(teamId: string, teamSession: ClientSession) { const result = await this.teamModel.findOneAndRemove( { @@ -76,7 +35,7 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { { session: teamSession } ); - if (!result) throw Error(DELETE_FAILED); + if (!result) throw new NotFoundException(DELETE_FAILED); return { _id: result._id }; } } From 5b001f68f89177ee0f05e9d47ae23f3dcf60f943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Thu, 24 Nov 2022 17:56:22 +0000 Subject: [PATCH 3/8] feat: added delete team endpoint --- .../applications/delete.team.application.ts | 16 ++++ .../teams/controller/team.controller.ts | 38 ++++++++- .../delete.team.application.interface.ts | 3 + .../services/delete.team.service.interface.ts | 4 + backend/src/modules/teams/interfaces/types.ts | 8 +- backend/src/modules/teams/providers.ts | 12 +++ .../teams/services/delete.team.service.ts | 82 +++++++++++++++++++ backend/src/modules/teams/teams.module.ts | 6 +- 8 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 backend/src/modules/teams/applications/delete.team.application.ts create mode 100644 backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts create mode 100644 backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts create mode 100644 backend/src/modules/teams/services/delete.team.service.ts diff --git a/backend/src/modules/teams/applications/delete.team.application.ts b/backend/src/modules/teams/applications/delete.team.application.ts new file mode 100644 index 000000000..006ef4fde --- /dev/null +++ b/backend/src/modules/teams/applications/delete.team.application.ts @@ -0,0 +1,16 @@ +import { Inject, Injectable } from '@nestjs/common'; + +import { DeleteTeamApplicationInterface } from '../interfaces/applications/delete.team.application.interface'; +import { TYPES } from '../interfaces/types'; + +@Injectable() +export class DeleteTeamApplication implements DeleteTeamApplicationInterface { + constructor( + @Inject(TYPES.services.DeleteTeamService) + private deleteTeamServices: DeleteTeamApplicationInterface + ) {} + + delete(teamId: string) { + return this.deleteTeamServices.delete(teamId); + } +} diff --git a/backend/src/modules/teams/controller/team.controller.ts b/backend/src/modules/teams/controller/team.controller.ts index cb8d55f52..664e957c1 100644 --- a/backend/src/modules/teams/controller/team.controller.ts +++ b/backend/src/modules/teams/controller/team.controller.ts @@ -2,6 +2,7 @@ import { BadRequestException, Body, Controller, + Delete, Get, Inject, Param, @@ -32,7 +33,7 @@ import { import { TeamParams } from 'src/libs/dto/param/team.params'; import { TeamQueryParams } from 'src/libs/dto/param/team.query.params'; import { TeamRoles } from 'src/libs/enum/team.roles'; -import { INSERT_FAILED, UPDATE_FAILED } from 'src/libs/exceptions/messages'; +import { DELETE_FAILED, INSERT_FAILED, UPDATE_FAILED } from 'src/libs/exceptions/messages'; import JwtAuthenticationGuard from 'src/libs/guards/jwtAuth.guard'; import RequestWithUser from 'src/libs/interfaces/requestWithUser.interface'; import { BadRequestResponse } from 'src/libs/swagger/errors/bad-request.swagger'; @@ -41,6 +42,7 @@ import { UnauthorizedResponse } from 'src/libs/swagger/errors/unauthorized.swagg import { TeamUserGuard } from '../../../libs/guards/teamRoles.guard'; import { ForbiddenResponse } from '../../../libs/swagger/errors/forbidden.swagger'; import { NotFoundResponse } from '../../../libs/swagger/errors/not-found.swagger'; +import { DeleteTeamApplication } from '../applications/delete.team.application'; import { UpdateTeamApplication } from '../applications/update.team.application'; import { CreateTeamDto } from '../dto/crate-team.dto'; import TeamDto from '../dto/team.dto'; @@ -62,7 +64,9 @@ export default class TeamsController { @Inject(TYPES.applications.GetTeamApplication) private getTeamApp: GetTeamApplicationInterface, @Inject(TYPES.applications.UpdateTeamApplication) - private updateTeamApp: UpdateTeamApplication + private updateTeamApp: UpdateTeamApplication, + @Inject(TYPES.applications.DeleteTeamApplication) + private deleteTeamApp: DeleteTeamApplication ) {} @ApiOperation({ summary: 'Create a new team' }) @@ -220,4 +224,34 @@ export default class TeamsController { return teamUser; } + + @ApiOperation({ summary: 'Delete a specific team' }) + @ApiParam({ type: String, name: 'teamId', required: true }) + @ApiOkResponse({ type: Boolean, description: 'Team successfully deleted!' }) + @ApiBadRequestResponse({ + description: 'Bad Request', + type: BadRequestResponse + }) + @ApiUnauthorizedResponse({ + description: 'Unauthorized', + type: UnauthorizedResponse + }) + @ApiInternalServerErrorResponse({ + description: 'Internal Server Error', + type: InternalServerErrorResponse + }) + @ApiForbiddenResponse({ + description: 'Forbidden', + type: ForbiddenResponse + }) + @TeamUser(TeamRoles.ADMIN) + @UseGuards(TeamUserGuard) + @Delete(':teamId') + async deleteTeam(@Param() { teamId }: TeamParams) { + const result = await this.deleteTeamApp.delete(teamId); + + if (!result) throw new BadRequestException(DELETE_FAILED); + + return result; + } } diff --git a/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts b/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts new file mode 100644 index 000000000..651ffa40c --- /dev/null +++ b/backend/src/modules/teams/interfaces/applications/delete.team.application.interface.ts @@ -0,0 +1,3 @@ +export interface DeleteTeamApplicationInterface { + delete(teamId: string): Promise; +} diff --git a/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts b/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts new file mode 100644 index 000000000..e13d4ae87 --- /dev/null +++ b/backend/src/modules/teams/interfaces/services/delete.team.service.interface.ts @@ -0,0 +1,4 @@ +export interface DeleteTeamServiceInterface { + // delete doesn't return an object + delete(teamId: string, userId: string): Promise; +} diff --git a/backend/src/modules/teams/interfaces/types.ts b/backend/src/modules/teams/interfaces/types.ts index 2e8f45e71..889d462ca 100644 --- a/backend/src/modules/teams/interfaces/types.ts +++ b/backend/src/modules/teams/interfaces/types.ts @@ -2,14 +2,14 @@ export const TYPES = { services: { CreateTeamService: 'CreateTeamService', GetTeamService: 'GetTeamService', - UpdateTeamService: 'UpdateTeamService' - // DeleteTeamService: 'DeleteTeamService', + UpdateTeamService: 'UpdateTeamService', + DeleteTeamService: 'DeleteTeamService' }, applications: { CreateTeamApplication: 'CreateTeamApplication', GetTeamApplication: 'GetTeamApplication', - UpdateTeamApplication: 'UpdateTeamApplication' - // DeleteBoardApplication: 'DeleteBoardApplication', + UpdateTeamApplication: 'UpdateTeamApplication', + DeleteTeamApplication: 'DeleteTeamApplication' // UpdateBoardApplication: 'UpdateBoardApplication', } }; diff --git a/backend/src/modules/teams/providers.ts b/backend/src/modules/teams/providers.ts index a448fbb14..d2cad2429 100644 --- a/backend/src/modules/teams/providers.ts +++ b/backend/src/modules/teams/providers.ts @@ -1,8 +1,10 @@ import { CreateTeamApplication } from './applications/create.team.application'; +import { DeleteTeamApplication } from './applications/delete.team.application'; import { GetTeamApplication } from './applications/get.team.application'; import { UpdateTeamApplication } from './applications/update.team.application'; import { TYPES } from './interfaces/types'; import CreateTeamService from './services/create.team.service'; +import DeleteTeamService from './services/delete.team.service'; import GetTeamService from './services/get.team.service'; import UpdateTeamService from './services/update.team.service'; @@ -35,3 +37,13 @@ export const updateTeamApplication = { provide: TYPES.applications.UpdateTeamApplication, useClass: UpdateTeamApplication }; + +export const deleteTeamService = { + provide: TYPES.services.DeleteTeamService, + useClass: DeleteTeamService +}; + +export const deleteTeamApplication = { + provide: TYPES.applications.DeleteTeamApplication, + useClass: DeleteTeamApplication +}; diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts new file mode 100644 index 000000000..6741b0842 --- /dev/null +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -0,0 +1,82 @@ +import { forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { ClientSession, LeanDocument, Model } from 'mongoose'; + +import { DELETE_FAILED } from 'libs/exceptions/messages'; + +import { DeleteTeamServiceInterface } from '../interfaces/services/delete.team.service.interface'; +import { GetTeamServiceInterface } from '../interfaces/services/get.team.service.interface'; +import * as Teams from '../interfaces/types'; +import Team, { TeamDocument } from '../schemas/teams.schema'; + +@Injectable() +export default class DeleteTeamService implements DeleteTeamServiceInterface { + constructor( + @InjectModel(Team.name) private teamModel: Model, + @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) + private getTeamService: GetTeamServiceInterface + ) {} + + async delete(teamId: string): Promise { + const team = await this.getTeam(teamId); + // no need ? + if (!team) { + throw new NotFoundException('Team not found!'); + } + + const teamSession = await this.teamModel.db.startSession(); + teamSession.startTransaction(); + try { + // delete team + await this.deleteTeam(teamId, teamSession); + await teamSession.commitTransaction(); + return true; + } catch (e) { + await teamSession.abortTransaction(); + } finally { + await teamSession.endSession(); + } + + return false; + } + + /* ----- HELPERS ------ */ + private async getTeam(teamId: string): Promise> { + const team = await this.getTeamService.getTeam(teamId); + if (!team) { + throw new NotFoundException('Team not found!'); + } + return team; + } + + // private async getUser(userId: string): Promise> { + // const user = await this.getUserService.getById(userId); + // if (!user) { + // throw new NotFoundException('User not found!'); + // } + // return user; + // } + + // private async getTeamUser( + // userId: string, + // teamId: string + // ): Promise> { + // const teamUser = await this.getTeamService.getTeamUser(userId, teamId); + // if (!teamUser) { + // throw new NotFoundException('User does not belong to team!'); + // } + // return teamUser; + // } + + private async deleteTeam(teamId: string, teamSession: ClientSession) { + const result = await this.teamModel.findOneAndRemove( + { + _id: teamId + }, + { session: teamSession } + ); + + if (!result) throw Error(DELETE_FAILED); + return { _id: result._id }; + } +} diff --git a/backend/src/modules/teams/teams.module.ts b/backend/src/modules/teams/teams.module.ts index 64fb41432..49ce31a2a 100644 --- a/backend/src/modules/teams/teams.module.ts +++ b/backend/src/modules/teams/teams.module.ts @@ -8,6 +8,8 @@ import TeamsController from './controller/team.controller'; import { createTeamApplication, createTeamService, + deleteTeamApplication, + deleteTeamService, getTeamApplication, getTeamService, updateTeamApplication, @@ -22,7 +24,9 @@ import { getTeamService, getTeamApplication, updateTeamService, - updateTeamApplication + updateTeamApplication, + deleteTeamApplication, + deleteTeamService ], controllers: [TeamsController], exports: [getTeamApplication, getTeamService, createTeamService, updateTeamService] From 13d12df62fd63dad2f1625ee7f9d5b7133bee79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 25 Nov 2022 10:04:39 +0000 Subject: [PATCH 4/8] fix: removed unnecessary throws --- .../teams/services/delete.team.service.ts | 49 ++----------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts index 6741b0842..ffc60113b 100644 --- a/backend/src/modules/teams/services/delete.team.service.ts +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -1,33 +1,20 @@ -import { forwardRef, Inject, Injectable, NotFoundException } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { ClientSession, LeanDocument, Model } from 'mongoose'; +import { ClientSession, Model } from 'mongoose'; import { DELETE_FAILED } from 'libs/exceptions/messages'; import { DeleteTeamServiceInterface } from '../interfaces/services/delete.team.service.interface'; -import { GetTeamServiceInterface } from '../interfaces/services/get.team.service.interface'; -import * as Teams from '../interfaces/types'; import Team, { TeamDocument } from '../schemas/teams.schema'; @Injectable() export default class DeleteTeamService implements DeleteTeamServiceInterface { - constructor( - @InjectModel(Team.name) private teamModel: Model, - @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) - private getTeamService: GetTeamServiceInterface - ) {} + constructor(@InjectModel(Team.name) private teamModel: Model) {} async delete(teamId: string): Promise { - const team = await this.getTeam(teamId); - // no need ? - if (!team) { - throw new NotFoundException('Team not found!'); - } - const teamSession = await this.teamModel.db.startSession(); teamSession.startTransaction(); try { - // delete team await this.deleteTeam(teamId, teamSession); await teamSession.commitTransaction(); return true; @@ -40,34 +27,6 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { return false; } - /* ----- HELPERS ------ */ - private async getTeam(teamId: string): Promise> { - const team = await this.getTeamService.getTeam(teamId); - if (!team) { - throw new NotFoundException('Team not found!'); - } - return team; - } - - // private async getUser(userId: string): Promise> { - // const user = await this.getUserService.getById(userId); - // if (!user) { - // throw new NotFoundException('User not found!'); - // } - // return user; - // } - - // private async getTeamUser( - // userId: string, - // teamId: string - // ): Promise> { - // const teamUser = await this.getTeamService.getTeamUser(userId, teamId); - // if (!teamUser) { - // throw new NotFoundException('User does not belong to team!'); - // } - // return teamUser; - // } - private async deleteTeam(teamId: string, teamSession: ClientSession) { const result = await this.teamModel.findOneAndRemove( { @@ -76,7 +35,7 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { { session: teamSession } ); - if (!result) throw Error(DELETE_FAILED); + if (!result) throw new NotFoundException(DELETE_FAILED); return { _id: result._id }; } } From 2fd0d326aabd7114d874037c20649928e3315fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 25 Nov 2022 12:39:39 +0000 Subject: [PATCH 5/8] fix: added missing src/ to imports --- backend/src/modules/teams/services/delete.team.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts index ffc60113b..cfde9c92c 100644 --- a/backend/src/modules/teams/services/delete.team.service.ts +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -1,9 +1,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { ClientSession, Model } from 'mongoose'; - -import { DELETE_FAILED } from 'libs/exceptions/messages'; - +import { DELETE_FAILED } from 'src/libs/exceptions/messages'; import { DeleteTeamServiceInterface } from '../interfaces/services/delete.team.service.interface'; import Team, { TeamDocument } from '../schemas/teams.schema'; @@ -17,6 +15,7 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { try { await this.deleteTeam(teamId, teamSession); await teamSession.commitTransaction(); + return true; } catch (e) { await teamSession.abortTransaction(); @@ -36,6 +35,7 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { ); if (!result) throw new NotFoundException(DELETE_FAILED); + return { _id: result._id }; } } From 1c6bd30bfe803f6784e37aa904420a11038ce8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 25 Nov 2022 18:15:00 +0000 Subject: [PATCH 6/8] fix: throw error if team is not found --- backend/src/libs/guards/teamRoles.guard.ts | 13 ++++++++----- .../src/modules/teams/controller/team.controller.ts | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/src/libs/guards/teamRoles.guard.ts b/backend/src/libs/guards/teamRoles.guard.ts index be4a0f7ae..c88c95744 100644 --- a/backend/src/libs/guards/teamRoles.guard.ts +++ b/backend/src/libs/guards/teamRoles.guard.ts @@ -1,4 +1,4 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; @@ -16,10 +16,13 @@ export class TeamUserGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const user = request.user; - const team: string = request.params.teamId; + const teamId: string = request.params.teamId; + try { + const userFound = await this.teamUserModel.findOne({ user: user._id, team: teamId }).exec(); - const userFound = await this.teamUserModel.findOne({ user: user._id, teamId: team }).exec(); - - return user.isSAdmin || permission === userFound?.role; + return user.isSAdmin || permission === userFound?.role; + } catch (error) { + throw new ForbiddenException(); + } } } diff --git a/backend/src/modules/teams/controller/team.controller.ts b/backend/src/modules/teams/controller/team.controller.ts index 664e957c1..44170d022 100644 --- a/backend/src/modules/teams/controller/team.controller.ts +++ b/backend/src/modules/teams/controller/team.controller.ts @@ -245,6 +245,7 @@ export default class TeamsController { type: ForbiddenResponse }) @TeamUser(TeamRoles.ADMIN) + // @TeamUser(TeamRoles.STAKEHOLDER) @UseGuards(TeamUserGuard) @Delete(':teamId') async deleteTeam(@Param() { teamId }: TeamParams) { From d9bb9342a29cc78cd16018a98bfd45ecd6185d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Mon, 28 Nov 2022 17:08:13 +0000 Subject: [PATCH 7/8] fix: removed unnecessary awaits --- .../src/modules/teams/controller/team.controller.ts | 5 ++--- .../modules/teams/services/delete.team.service.ts | 12 +++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/teams/controller/team.controller.ts b/backend/src/modules/teams/controller/team.controller.ts index cfb9fa6b5..e335d03d9 100644 --- a/backend/src/modules/teams/controller/team.controller.ts +++ b/backend/src/modules/teams/controller/team.controller.ts @@ -245,10 +245,9 @@ export default class TeamsController { type: ForbiddenResponse }) @TeamUser(TeamRoles.ADMIN) - // @TeamUser(TeamRoles.STAKEHOLDER) @UseGuards(TeamUserGuard) @Delete(':teamId') - async deleteTeam(@Param() { teamId }: TeamParams) { - return await this.deleteTeamApp.delete(teamId); + deleteTeam(@Param() { teamId }: TeamParams) { + return this.deleteTeamApp.delete(teamId); } } diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts index b76b57e06..60903112a 100644 --- a/backend/src/modules/teams/services/delete.team.service.ts +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -16,17 +16,23 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { async delete(teamId: string): Promise { const teamSession = await this.teamModel.db.startSession(); teamSession.startTransaction(); + const teamUserSession = await this.teamModel.db.startSession(); + teamUserSession.startTransaction(); + try { await this.deleteTeam(teamId, teamSession); - await this.deleteTeamUsers(teamId, teamSession); + await this.deleteTeamUsers(teamId, teamUserSession); await teamSession.commitTransaction(); + await teamUserSession.commitTransaction(); return true; } catch (e) { await teamSession.abortTransaction(); + await teamUserSession.abortTransaction(); } finally { await teamSession.endSession(); + await teamUserSession.endSession(); } throw new BadRequestException(DELETE_FAILED); } @@ -42,13 +48,13 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { if (!result) throw new NotFoundException(DELETE_FAILED); } - private async deleteTeamUsers(teamId: string, teamSession: ClientSession) { + private async deleteTeamUsers(teamId: string, teamUserSession: ClientSession) { const { deletedCount } = await this.teamUserModel .deleteMany( { team: teamId }, - { session: teamSession } + { session: teamUserSession } ) .exec(); From db6f313e95f7376c5002a51edfc087e51147b5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Mon, 28 Nov 2022 17:45:44 +0000 Subject: [PATCH 8/8] fix: renamed a variable --- backend/src/modules/teams/services/delete.team.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/modules/teams/services/delete.team.service.ts b/backend/src/modules/teams/services/delete.team.service.ts index 60903112a..7b993998e 100644 --- a/backend/src/modules/teams/services/delete.team.service.ts +++ b/backend/src/modules/teams/services/delete.team.service.ts @@ -16,7 +16,7 @@ export default class DeleteTeamService implements DeleteTeamServiceInterface { async delete(teamId: string): Promise { const teamSession = await this.teamModel.db.startSession(); teamSession.startTransaction(); - const teamUserSession = await this.teamModel.db.startSession(); + const teamUserSession = await this.teamUserModel.db.startSession(); teamUserSession.startTransaction(); try {