From c4ebfb699e4fb5787a19bce947978579ad8fde76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Tue, 28 Feb 2023 14:35:27 +0000 Subject: [PATCH 01/22] feat: start of board user repository --- .../mongo/mongo-generic.repository.ts | 4 ++-- backend/src/modules/boards/boards.module.ts | 2 ++ .../src/modules/boards/boards.providers.ts | 6 ++++++ .../src/modules/boards/interfaces/types.ts | 3 ++- .../board-user.repository.interface.ts | 6 ++++++ .../repositories/board-user.repository.ts | 19 +++++++++++++++++++ .../board.repository.interface.ts | 1 + .../boards/repositories/board.repository.ts | 5 +++++ .../boards/services/get.board.service.ts | 12 ++++++------ 9 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 backend/src/modules/boards/repositories/board-user.repository.interface.ts create mode 100644 backend/src/modules/boards/repositories/board-user.repository.ts diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 12643ad5f..54c9b669b 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -34,8 +34,8 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .exec() as Promise; } - findOneByField(value: ModelProps): Promise { - return this._repository.findOne(value).exec(); + findOneByField(value: ModelProps, selectedValues?: SelectedValues): Promise { + return this._repository.findOne(value).select(selectedValues).exec(); } findAllWithQuery( diff --git a/backend/src/modules/boards/boards.module.ts b/backend/src/modules/boards/boards.module.ts index 72fc0acf4..98542405e 100644 --- a/backend/src/modules/boards/boards.module.ts +++ b/backend/src/modules/boards/boards.module.ts @@ -18,6 +18,7 @@ import { afterUserUpdatedDurationSubscriber, boardRepository, boardTimerRepository, + boardUserRepository, createBoardApplication, createBoardService, deleteBoardApplication, @@ -69,6 +70,7 @@ import PublicBoardsController from './controller/public.boards.controller'; afterUserUpdatedDurationSubscriber, afterUserRequestedTimerStateSubscriber, boardRepository, + boardUserRepository, userRepository ], controllers: [BoardsController, PublicBoardsController], diff --git a/backend/src/modules/boards/boards.providers.ts b/backend/src/modules/boards/boards.providers.ts index 1f9e5e162..9b341d732 100644 --- a/backend/src/modules/boards/boards.providers.ts +++ b/backend/src/modules/boards/boards.providers.ts @@ -15,6 +15,7 @@ import { DeleteBoardApplication } from './applications/delete.board.application' import { GetBoardApplication } from './applications/get.board.application'; import { UpdateBoardApplication } from './applications/update.board.application'; import { TYPES } from './interfaces/types'; +import { BoardUserRepository } from './repositories/board-user.repository'; import { BoardRepository } from './repositories/board.repository'; import CreateBoardServiceImpl from './services/create.board.service'; import DeleteBoardServiceImpl from './services/delete.board.service'; @@ -125,3 +126,8 @@ export const boardRepository = { provide: TYPES.repositories.BoardRepository, useClass: BoardRepository }; + +export const boardUserRepository = { + provide: TYPES.repositories.BoardUserRepository, + useClass: BoardUserRepository +}; diff --git a/backend/src/modules/boards/interfaces/types.ts b/backend/src/modules/boards/interfaces/types.ts index 0330f02d8..8badd5f56 100644 --- a/backend/src/modules/boards/interfaces/types.ts +++ b/backend/src/modules/boards/interfaces/types.ts @@ -19,7 +19,8 @@ export const TYPES = { }, repositories: { BoardTimerRepository: 'BoardTimerRepository', - BoardRepository: 'BoardRepository' + BoardRepository: 'BoardRepository', + BoardUserRepository: 'BoardUserRepository' }, subscribers: { AfterUserPausedTimerSubscriber: 'AfterUserPausedTimerSubscriber', diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts new file mode 100644 index 000000000..e85657bdb --- /dev/null +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -0,0 +1,6 @@ +import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; +import BoardUser from '../entities/board.user.schema'; + +export interface BoardUserRepositoryInterface extends BaseInterfaceRepository { + getAllBoardsIdsOfUser(userId: string): Promise; +} diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts new file mode 100644 index 000000000..d93c2ce44 --- /dev/null +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@nestjs/common'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; +import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; +import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; +import { BoardUserRepositoryInterface } from './board-user.repository.interface'; + +@Injectable() +export class BoardUserRepository + extends MongoGenericRepository + implements BoardUserRepositoryInterface +{ + constructor(@InjectModel(BoardUser.name) private model: Model) { + super(model); + } + getAllBoardsIdsOfUser(userId: string) { + return this.model.find({ user: userId }).select('board').exec(); + } +} diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 9bcc246ad..3a28bd6d9 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -3,4 +3,5 @@ import Board from 'src/modules/boards/entities/board.schema'; export interface BoardRepositoryInterface extends BaseInterfaceRepository { getBoard(boardId: string): Promise; + getBoardFromRepo(boardId: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index e7b7f7fa5..5cabd3c08 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -3,6 +3,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; +import { BoardDataPopulate } from '../utils/populate-board'; import { BoardRepositoryInterface } from './board.repository.interface'; @Injectable() @@ -16,4 +17,8 @@ export class BoardRepository getBoard(boardId: string): Promise { return this.findOneById(boardId); } + + getBoardFromRepo(boardId: string) { + return this.findOneById(boardId, {}, BoardDataPopulate); + } } diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 20fc0f999..6b5d5d3c7 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -19,6 +19,8 @@ import Board, { BoardDocument } from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { cleanBoard } from '../utils/clean-board'; import { BoardDataPopulate, GetBoardDataPopulate } from '../utils/populate-board'; +import { TYPES } from '../interfaces/types'; +import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { @@ -29,18 +31,16 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { @Inject(forwardRef(() => Team.TYPES.services.GetTeamService)) private getTeamService: GetTeamServiceInterface, @Inject(Users.TYPES.repository) - private readonly userRepository: UserRepositoryInterface + private readonly userRepository: UserRepositoryInterface, + @Inject(TYPES.repositories.BoardUserRepository) + private readonly boardUserRepository: BoardUserRepositoryInterface ) {} private readonly logger = new Logger(GetBoardServiceImpl.name); - getAllBoardsIdsOfUser(userId: string) { - return this.boardUserModel.find({ user: userId }).select('board').lean().exec(); - } - async getAllBoardIdsAndTeamIdsOfUser(userId: string) { const [boardIds, teamIds] = await Promise.all([ - this.getAllBoardsIdsOfUser(userId), + this.boardUserRepository.getAllBoardsIdsOfUser(userId), this.getTeamService.getTeamsOfUser(userId) ]); From 6eb9251ebc36f67405d2ef6fae098f99007c6f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Tue, 28 Feb 2023 17:53:03 +0000 Subject: [PATCH 02/22] feat: add get mainboard functions to repository --- .../mongo/mongo-generic.repository.ts | 12 +++++-- .../services/get.board.service.interface.ts | 2 -- .../board.repository.interface.ts | 4 ++- .../boards/repositories/board.repository.ts | 16 +++++++-- .../boards/services/create.board.service.ts | 7 ++-- .../boards/services/delete.board.service.ts | 5 +-- .../boards/services/get.board.service.ts | 36 +++++++------------ .../src/modules/schedules/schedules.module.ts | 15 ++++++-- .../services/create.schedules.service.ts | 8 +++-- 9 files changed, 66 insertions(+), 39 deletions(-) diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 54c9b669b..524d09068 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -34,8 +34,16 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .exec() as Promise; } - findOneByField(value: ModelProps, selectedValues?: SelectedValues): Promise { - return this._repository.findOne(value).select(selectedValues).exec(); + findOneByField( + value: ModelProps | FilterQuery, + selectedValues?: SelectedValues, + populate?: PopulateType + ): Promise { + return this._repository + .findOne(value) + .select(selectedValues) + .populate(populate) + .exec() as Promise; } findAllWithQuery( diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 71a9bddda..fe9006559 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -21,8 +21,6 @@ export interface GetBoardServiceInterface { size?: number ): Promise; - getBoardFromRepo(boardId: string): Promise; - getBoardData(boardId: string): Promise; getBoard( diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 3a28bd6d9..9f6cfdc56 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -3,5 +3,7 @@ import Board from 'src/modules/boards/entities/board.schema'; export interface BoardRepositoryInterface extends BaseInterfaceRepository { getBoard(boardId: string): Promise; - getBoardFromRepo(boardId: string): Promise; + getBoardPopulated(boardId: string): Promise; + getMainBoard(boardId: string): Promise; + getBoardData(boardId: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 5cabd3c08..45604313c 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -3,7 +3,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; -import { BoardDataPopulate } from '../utils/populate-board'; +import { BoardDataPopulate, GetBoardDataPopulate } from '../utils/populate-board'; import { BoardRepositoryInterface } from './board.repository.interface'; @Injectable() @@ -18,7 +18,19 @@ export class BoardRepository return this.findOneById(boardId); } - getBoardFromRepo(boardId: string) { + getBoardPopulated(boardId: string) { return this.findOneById(boardId, {}, BoardDataPopulate); } + + getMainBoard(boardId: string) { + return this.findOneByField({ dividedBoards: { $in: boardId } }, 'title'); + } + + getBoardData(boardId: string) { + return this.findOneById( + boardId, + '-slackEnable -slackChannelId -recurrent -__v', + GetBoardDataPopulate + ); + } } diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index 37ad0bdf9..418c2c6f4 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -29,6 +29,7 @@ import Board, { BoardDocument } from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { UpdateTeamServiceInterface } from 'src/modules/teams/interfaces/services/update.team.service.interface'; import { addDays, addMonths, isAfter } from 'date-fns'; +import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; export interface CreateBoardDto { maxUsers: number; @@ -54,7 +55,9 @@ export default class CreateBoardServiceImpl implements CreateBoardService { @Inject(SchedulesType.TYPES.services.CreateSchedulesService) private createSchedulesService: CreateSchedulesServiceInterface, @Inject(CommunicationsType.TYPES.services.SlackCommunicationService) - private slackCommunicationService: CommunicationServiceInterface + private slackCommunicationService: CommunicationServiceInterface, + @Inject(TYPES.repositories.BoardRepository) + private readonly boardRepository: BoardRepositoryInterface ) {} saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) { @@ -183,7 +186,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { this.logger.verbose(`Communication Slack Enable is set to "${boardData.slackEnable}".`); if (slackEnable && team && teamData.name === 'xgeeks') { - const populatedBoard = await this.getBoardService.getBoardFromRepo(newBoard._id); + const populatedBoard = await this.boardRepository.getBoardPopulated(newBoard._id); if (populatedBoard) { this.logger.verbose(`Call Slack Communication Service for board id "${newBoard._id}".`); diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index fdd74cc7e..f70f19cd1 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -167,8 +167,9 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa // if slack is enable for the deleted board if (slackEnable) { // archive all related channels - // for that we need to fecth the board with all dividedBoards - const board = await this.getBoardService.getBoardFromRepo(boardId); + // for that we need to fetch the board with all dividedBoards + + const board = await this.boardRepository.getBoardPopulated(boardId); this.archiveChannelService.execute({ type: ArchiveChannelDataOptions.BOARD, diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 6b5d5d3c7..96c05cd42 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -18,9 +18,10 @@ import { GetBoardServiceInterface } from '../interfaces/services/get.board.servi import Board, { BoardDocument } from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { cleanBoard } from '../utils/clean-board'; -import { BoardDataPopulate, GetBoardDataPopulate } from '../utils/populate-board'; +import { GetBoardDataPopulate } from '../utils/populate-board'; import { TYPES } from '../interfaces/types'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; +import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { @@ -33,7 +34,9 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { @Inject(Users.TYPES.repository) private readonly userRepository: UserRepositoryInterface, @Inject(TYPES.repositories.BoardUserRepository) - private readonly boardUserRepository: BoardUserRepositoryInterface + private readonly boardUserRepository: BoardUserRepositoryInterface, + @Inject(TYPES.repositories.BoardRepository) + private readonly boardRepository: BoardRepositoryInterface ) {} private readonly logger = new Logger(GetBoardServiceImpl.name); @@ -166,16 +169,6 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return { boards: [], hasNextPage, page }; } - async getBoardFromRepo(boardId: string) { - const board = await this.boardModel - .findById(boardId) - .populate(BoardDataPopulate) - .lean({ virtuals: true }) - .exec(); - - return board as Board; - } - async getMainBoardData(boardId: string) { const mainBoard = await this.boardModel .findOne({ dividedBoards: { $in: boardId } }) @@ -202,19 +195,16 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return mainBoard; } - async getMainBoard(boardId: string) { - const mainBoard = await this.boardModel - .findOne({ dividedBoards: { $in: boardId } }) - .select('title') - .lean() - .exec(); - - return mainBoard; - } - async getBoard(boardId: string, userId: string) { let board = await this.getBoardData(boardId); + // TODO os resultados não estão iguas, quando vem do repositorio vem com mais campos: addCards, postAnonimously + /// console.log('board', board); + + // let board1 = await this.boardRepository.getBoardData(boardId); + + // console.log('board1', board1); + if (!board) throw new NotFoundException(NOT_FOUND); const userFound = await this.userRepository.getById(userId); @@ -226,7 +216,7 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { board = cleanBoard(board, userId); if (board.isSubBoard) { - const mainBoard = await this.getMainBoard(boardId); + const mainBoard = await this.boardRepository.getMainBoard(boardId); return { board, mainBoard }; } diff --git a/backend/src/modules/schedules/schedules.module.ts b/backend/src/modules/schedules/schedules.module.ts index df37cb30e..4ae0217b2 100644 --- a/backend/src/modules/schedules/schedules.module.ts +++ b/backend/src/modules/schedules/schedules.module.ts @@ -1,12 +1,21 @@ import { Module, forwardRef } from '@nestjs/common'; -import { mongooseSchedulesModule } from 'src/infrastructure/database/mongoose.module'; +import { + mongooseBoardModule, + mongooseSchedulesModule +} from 'src/infrastructure/database/mongoose.module'; import BoardsModule from 'src/modules/boards/boards.module'; import { CommunicationModule } from 'src/modules/communication/communication.module'; +import { boardRepository } from '../boards/boards.providers'; import { createSchedulesService, deleteSchedulesService } from './schedules.providers'; @Module({ - imports: [mongooseSchedulesModule, forwardRef(() => BoardsModule), CommunicationModule], - providers: [createSchedulesService, deleteSchedulesService], + imports: [ + mongooseSchedulesModule, + mongooseBoardModule, + forwardRef(() => BoardsModule), + CommunicationModule + ], + providers: [createSchedulesService, deleteSchedulesService, boardRepository], exports: [createSchedulesService, deleteSchedulesService] }) export class SchedulesModule {} diff --git a/backend/src/modules/schedules/services/create.schedules.service.ts b/backend/src/modules/schedules/services/create.schedules.service.ts index c39524f8b..5d0d3f5d1 100644 --- a/backend/src/modules/schedules/services/create.schedules.service.ts +++ b/backend/src/modules/schedules/services/create.schedules.service.ts @@ -22,6 +22,7 @@ import { import { DeleteSchedulesServiceInterface } from '../interfaces/services/delete.schedules.service.interface'; import { TYPES } from '../interfaces/types'; import Schedules, { SchedulesDocument } from '../schemas/schedules.schema'; +import { BoardRepositoryInterface } from 'src/modules/boards/repositories/board.repository.interface'; @Injectable() export class CreateSchedulesService implements CreateSchedulesServiceInterface { @@ -38,7 +39,9 @@ export class CreateSchedulesService implements CreateSchedulesServiceInterface { private getBoardService: GetBoardServiceInterface, private schedulerRegistry: SchedulerRegistry, @Inject(CommunicationTypes.TYPES.services.SlackArchiveChannelService) - private archiveChannelService: ArchiveChannelServiceInterface + private archiveChannelService: ArchiveChannelServiceInterface, + @Inject(BoardTypes.TYPES.repositories.BoardRepository) + private readonly boardRepository: BoardRepositoryInterface ) { this.createInitialJobs(); } @@ -110,7 +113,8 @@ export class CreateSchedulesService implements CreateSchedulesServiceInterface { const deletedSchedule = await this.deleteSchedulesService.findAndDeleteScheduleByBoardId( oldBoardId ); - const oldBoard = await this.getBoardService.getBoardFromRepo(oldBoardId); + + const oldBoard = await this.boardRepository.getBoardPopulated(oldBoardId); if (!oldBoard) { await this.deleteSchedulesService.deleteScheduleByBoardId(oldBoardId); From ebdfa87cd3c53def90c5a621bdc3d25eedd10591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Wed, 1 Mar 2023 15:36:16 +0000 Subject: [PATCH 03/22] feat: add getBoard queries to board repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrícia Dias --- .../mongo/mongo-generic.repository.ts | 6 +- .../mocks/factories/board-factory.mock.ts | 2 + .../services/get.board.service.interface.ts | 16 ++-- .../repositories/board-user.repository.ts | 2 +- .../board.repository.interface.ts | 2 + .../boards/repositories/board.repository.ts | 27 ++++++ .../boards/services/create.board.service.ts | 2 +- .../boards/services/delete.board.service.ts | 2 +- .../boards/services/get.board.service.ts | 65 +++----------- .../services/update.column.service.spec.ts | 86 +++++++++++++------ .../columns/services/update.column.service.ts | 10 +-- 11 files changed, 120 insertions(+), 100 deletions(-) diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 524d09068..23d96f88f 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -31,6 +31,7 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .findById(id) .select(selectedValues) .populate(populate) + .lean({ virtuals: true }) .exec() as Promise; } @@ -49,13 +50,14 @@ export class MongoGenericRepository implements BaseInterfaceRepository { findAllWithQuery( query: FilterQuery, selectedValues?: SelectedValues, - populate?: PopulateType + populate?: PopulateType, + virtuals = true ): Promise { return this._repository .find(query) .select(selectedValues) .populate(populate) - .lean({ virtuals: true }) + .lean({ virtuals: virtuals }) .exec() as unknown as Promise; } diff --git a/backend/src/libs/test-utils/mocks/factories/board-factory.mock.ts b/backend/src/libs/test-utils/mocks/factories/board-factory.mock.ts index ee3d3c772..d049779ef 100644 --- a/backend/src/libs/test-utils/mocks/factories/board-factory.mock.ts +++ b/backend/src/libs/test-utils/mocks/factories/board-factory.mock.ts @@ -26,6 +26,8 @@ const mockBoardData = (countColumns = 2, countCards = 1, params?: Partial addCards: faker.datatype.boolean(), responsibles: ['1'], createdBy: userId, + addcards: faker.datatype.boolean(), + postAnonymously: faker.datatype.boolean(), ...params }; }; diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index fe9006559..f176050c9 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -1,4 +1,4 @@ -import { Document, LeanDocument } from 'mongoose'; +import { LeanDocument } from 'mongoose'; import Board, { BoardDocument } from '../../entities/board.schema'; import { BoardsAndPage } from '../boards-page.interface'; @@ -21,8 +21,6 @@ export interface GetBoardServiceInterface { size?: number ): Promise; - getBoardData(boardId: string): Promise; - getBoard( boardId: string, userId: string @@ -36,17 +34,15 @@ export interface GetBoardServiceInterface { | null >; - getMainBoardData( - boardId: string - ): Promise & { _id: any }> | null>; - countBoards(userId: string): Promise; - getAllBoardIdsAndTeamIdsOfUser( - userId: string - ): Promise<{ boardIds: LeanDocument[]; teamIds: any[] }>; + getAllBoardIdsAndTeamIdsOfUser(userId: string): Promise<{ boardIds: any[]; teamIds: any[] }>; getAllBoardsByTeamId(teamId: string): Promise[]>; + getBoardPopulated(boardId: string): Promise; + isBoardPublic(boardId: string): Promise; + + getBoardById(boardId: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index d93c2ce44..63e8143cf 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -14,6 +14,6 @@ export class BoardUserRepository super(model); } getAllBoardsIdsOfUser(userId: string) { - return this.model.find({ user: userId }).select('board').exec(); + return this.model.find({ user: userId }).select('board').lean().exec(); } } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 9f6cfdc56..9b5332620 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -6,4 +6,6 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository getBoardPopulated(boardId: string): Promise; getMainBoard(boardId: string): Promise; getBoardData(boardId: string): Promise; + getAllBoardsByTeamId(teamId: string): Promise; + countBoards(boardIds: string[], teamIds: string[]): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 45604313c..fe8d70278 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -3,6 +3,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; +import { QueryType } from '../interfaces/findQuery'; import { BoardDataPopulate, GetBoardDataPopulate } from '../utils/populate-board'; import { BoardRepositoryInterface } from './board.repository.interface'; @@ -14,6 +15,9 @@ export class BoardRepository constructor(@InjectModel(Board.name) private model: Model) { super(model); } + + /* Get Boards */ + getBoard(boardId: string): Promise { return this.findOneById(boardId); } @@ -33,4 +37,27 @@ export class BoardRepository GetBoardDataPopulate ); } + + getAllBoardsByTeamId(teamId: string) { + return this.findAllWithQuery({ team: teamId }, 'board', undefined, false); + } + + countBoards(boardIds: string[], teamIds: string[]) { + return this.model + .countDocuments({ + $and: [ + { isSubBoard: false }, + { $or: [{ _id: { $in: boardIds } }, { team: { $in: teamIds } }] } + ] + }) + .exec(); + } + + getCountPage(query: QueryType) { + return this.model.find(query).countDocuments().exec(); + } + + // getAllBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { + // return this.model.find(query).sort(); + // } } diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index 418c2c6f4..ca5093a89 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -186,7 +186,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { this.logger.verbose(`Communication Slack Enable is set to "${boardData.slackEnable}".`); if (slackEnable && team && teamData.name === 'xgeeks') { - const populatedBoard = await this.boardRepository.getBoardPopulated(newBoard._id); + const populatedBoard = await this.getBoardService.getBoardPopulated(newBoard._id); if (populatedBoard) { this.logger.verbose(`Call Slack Communication Service for board id "${newBoard._id}".`); diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index f70f19cd1..1e1648e2b 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -169,7 +169,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa // archive all related channels // for that we need to fetch the board with all dividedBoards - const board = await this.boardRepository.getBoardPopulated(boardId); + const board = await this.getBoardService.getBoardPopulated(boardId); this.archiveChannelService.execute({ type: ArchiveChannelDataOptions.BOARD, diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 96c05cd42..6869287a2 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -16,9 +16,7 @@ import * as Users from 'src/modules/users/interfaces/types'; import { QueryType } from '../interfaces/findQuery'; import { GetBoardServiceInterface } from '../interfaces/services/get.board.service.interface'; import Board, { BoardDocument } from '../entities/board.schema'; -import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { cleanBoard } from '../utils/clean-board'; -import { GetBoardDataPopulate } from '../utils/populate-board'; import { TYPES } from '../interfaces/types'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; @@ -27,8 +25,6 @@ import { BoardRepositoryInterface } from '../repositories/board.repository.inter export default class GetBoardServiceImpl implements GetBoardServiceInterface { constructor( @InjectModel(Board.name) private boardModel: Model, - @InjectModel(BoardUser.name) - private boardUserModel: Model, @Inject(forwardRef(() => Team.TYPES.services.GetTeamService)) private getTeamService: GetTeamServiceInterface, @Inject(Users.TYPES.repository) @@ -48,7 +44,7 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { ]); return { - boardIds: boardIds.map((boardUser) => boardUser.board), + boardIds: boardIds.map((boardUser) => String(boardUser.board)), teamIds: teamIds.map((team) => team._id) }; } @@ -169,41 +165,8 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return { boards: [], hasNextPage, page }; } - async getMainBoardData(boardId: string) { - const mainBoard = await this.boardModel - .findOne({ dividedBoards: { $in: boardId } }) - .select('dividedBoards team title') - .populate({ - path: 'dividedBoards', - select: '_id title' - }) - .populate({ - path: 'team', - select: 'name users _id', - populate: { - path: 'users', - select: 'user role', - populate: { - path: 'user', - select: 'firstName email lastName joinedAt' - } - } - }) - .lean({ virtuals: true }) - .exec(); - - return mainBoard; - } - async getBoard(boardId: string, userId: string) { - let board = await this.getBoardData(boardId); - - // TODO os resultados não estão iguas, quando vem do repositorio vem com mais campos: addCards, postAnonimously - /// console.log('board', board); - - // let board1 = await this.boardRepository.getBoardData(boardId); - - // console.log('board1', board1); + let board = await this.boardRepository.getBoardData(boardId); if (!board) throw new NotFoundException(NOT_FOUND); @@ -227,27 +190,19 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { async countBoards(userId: string) { const { boardIds, teamIds } = await this.getAllBoardIdsAndTeamIdsOfUser(userId); - return this.boardModel.countDocuments({ - $and: [ - { isSubBoard: false }, - { $or: [{ _id: { $in: boardIds } }, { team: { $in: teamIds } }] } - ] - }); + return await this.boardRepository.countBoards(boardIds, teamIds); } - async getBoardData(boardId: string) { - const board = await this.boardModel - .findById(boardId) - .select('-slackEnable -slackChannelId -recurrent -__v') - .populate(GetBoardDataPopulate) - .lean({ virtuals: true }) - .exec(); + getAllBoardsByTeamId(teamId: string) { + return this.boardRepository.getAllBoardsByTeamId(teamId); + } - return board as Board; + getBoardPopulated(boardId: string) { + return this.boardRepository.getBoardPopulated(boardId); } - getAllBoardsByTeamId(teamId: string) { - return this.boardModel.find({ team: teamId }).select('board').lean().exec(); + getBoardById(boardId: string) { + return this.boardRepository.getBoard(boardId); } async isBoardPublic(boardId: string) { diff --git a/backend/src/modules/columns/services/update.column.service.spec.ts b/backend/src/modules/columns/services/update.column.service.spec.ts index ec7d5eaf3..847ae7f35 100644 --- a/backend/src/modules/columns/services/update.column.service.spec.ts +++ b/backend/src/modules/columns/services/update.column.service.spec.ts @@ -4,9 +4,12 @@ import { BadRequestException, Logger, NotFoundException } from '@nestjs/common'; import { getModelToken } from '@nestjs/mongoose'; import { Test, TestingModule } from '@nestjs/testing'; import { BoardFactory } from 'src/libs/test-utils/mocks/factories/board-factory.mock'; -import { boardRepository } from 'src/modules/boards/boards.providers'; +import { + boardRepository, + boardUserRepository, + getBoardService +} from 'src/modules/boards/boards.providers'; import Board from 'src/modules/boards/entities/board.schema'; -import { BoardRepository } from 'src/modules/boards/repositories/board.repository'; import { deleteCardService, getCardService } from 'src/modules/cards/cards.providers'; import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; import { deleteVoteService } from 'src/modules/votes/votes.providers'; @@ -17,6 +20,9 @@ import * as Cards from 'src/modules/cards/interfaces/types'; import { ColumnRepository } from '../repositories/column.repository'; import UpdateColumnServiceImpl from './update.column.service'; import DeleteCardServiceImpl from 'src/modules/cards/services/delete.card.service'; +import GetBoardServiceImpl from 'src/modules/boards/services/get.board.service'; +import { getTeamService, teamRepository, teamUserRepository } from 'src/modules/teams/providers'; +import { userRepository } from 'src/modules/users/users.providers'; const fakeBoards = BoardFactory.createMany(2, 3, 2); @@ -24,8 +30,8 @@ describe('UpdateColumnService', () => { let columnService: UpdateColumnServiceImpl; let deleteCardServiceImpl: DeleteCardServiceImpl; let repositoryColumn: ColumnRepository; - let repositoryBoard: BoardRepository; let socketService: SocketGateway; + let getBoardServiceImpl: GetBoardServiceImpl; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -34,12 +40,19 @@ describe('UpdateColumnService', () => { UpdateColumnServiceImpl, DeleteCardServiceImpl, SocketGateway, + GetBoardServiceImpl, + getTeamService, updateColumnService, deleteCardService, + getBoardService, getCardService, deleteVoteService, columnRepository, + userRepository, boardRepository, + boardUserRepository, + teamRepository, + teamUserRepository, { provide: getModelToken(Board.name), useValue: {} @@ -47,6 +60,18 @@ describe('UpdateColumnService', () => { { provide: getModelToken('BoardUser'), useValue: {} + }, + { + provide: getModelToken('User'), + useValue: {} + }, + { + provide: getModelToken('Team'), + useValue: {} + }, + { + provide: getModelToken('TeamUser'), + useValue: {} } ] }).compile(); @@ -56,8 +81,8 @@ describe('UpdateColumnService', () => { Cards.TYPES.services.DeleteCardService ); repositoryColumn = module.get(Columns.TYPES.repositories.ColumnRepository); - repositoryBoard = module.get(Boards.TYPES.repositories.BoardRepository); socketService = module.get(SocketGateway); + getBoardServiceImpl = module.get(Boards.TYPES.services.GetBoardService); jest.spyOn(Logger.prototype, 'error').mockImplementation(jest.fn); }); @@ -133,27 +158,36 @@ describe('UpdateColumnService', () => { const fakeBoards = BoardFactory.createMany(2, 3, 2); const boardId = fakeBoards[1]._id; const boardResult = fakeBoards[1]; - const boardUpdateResult = { ...fakeBoards[1], cards: [] }; + const columnsResult = fakeBoards[1].columns.map((col) => { + if (col._id === fakeBoards[1].columns[0]._id) { + return { ...col, cards: [] }; + } + + return col; + }); + + const boardUpdateResult = { ...fakeBoards[1], columns: columnsResult }; const columnToDeleteCards = { id: fakeBoards.find((board) => board._id === boardId).columns[0]._id, socketId: faker.datatype.uuid() }; - //tests if board is found - const spyBoardRepository = jest - .spyOn(repositoryBoard, 'getBoard') - .mockResolvedValue(boardResult as unknown as ReturnType); - - const board = await repositoryBoard.getBoard(boardId); + const spyGetBoardService = jest + .spyOn(getBoardServiceImpl, 'getBoardById') + .mockResolvedValue( + boardResult as unknown as ReturnType + ); + const board = await getBoardServiceImpl.getBoardById(boardId); - expect(spyBoardRepository).toHaveBeenCalledWith(boardId); + expect(spyGetBoardService).toHaveBeenCalledWith(boardId); + expect(spyGetBoardService).toBeCalledTimes(1); expect(board).toEqual(boardResult); //test if board is updated const spyColumnRepository = jest .spyOn(repositoryColumn, 'deleteCards') .mockResolvedValue( - boardUpdateResult as unknown as ReturnType + boardUpdateResult as unknown as ReturnType ); jest.spyOn(deleteCardServiceImpl, 'deleteCardVotesFromColumn').mockResolvedValue(null); @@ -173,14 +207,16 @@ describe('UpdateColumnService', () => { socketId: faker.datatype.uuid() }; - const spyBoardRepository = jest.spyOn(repositoryBoard, 'getBoard').mockResolvedValue(null); + const spyGetBoardService = jest + .spyOn(getBoardServiceImpl, 'getBoardById') + .mockResolvedValue(null); expect(async () => { return await columnService.deleteCardsFromColumn(boardId, columnToDeleteCards); }).rejects.toThrow(BadRequestException); - expect(spyBoardRepository).toHaveBeenCalledWith(boardId); - expect(spyBoardRepository).toHaveBeenCalledTimes(1); + expect(spyGetBoardService).toHaveBeenCalledWith(boardId); + expect(spyGetBoardService).toHaveBeenCalledTimes(1); }); it("when given column_id doesn't exist, throw Bad Request Exception", async () => { @@ -191,11 +227,11 @@ describe('UpdateColumnService', () => { socketId: faker.datatype.uuid() }; - const spyBoardRepository = jest - .spyOn(repositoryBoard, 'getBoard') + const spyGetBoardService = jest + .spyOn(getBoardServiceImpl, 'getBoardById') .mockResolvedValue( fakeBoards.find((board) => boardId === board._id) as unknown as ReturnType< - typeof repositoryBoard.getBoard + typeof getBoardServiceImpl.getBoardById > ); @@ -203,8 +239,8 @@ describe('UpdateColumnService', () => { return await columnService.deleteCardsFromColumn(boardId, columnToDeleteCards); }).rejects.toThrow(NotFoundException); - expect(spyBoardRepository).toHaveBeenCalledWith(boardId); - expect(spyBoardRepository).toHaveBeenCalledTimes(1); + expect(spyGetBoardService).toHaveBeenCalledWith(boardId); + expect(spyGetBoardService).toHaveBeenCalledTimes(1); }); it("when board returned after deleting column cards doesn't exist, throw Bad Request Exception", async () => { @@ -214,11 +250,11 @@ describe('UpdateColumnService', () => { socketId: faker.datatype.uuid() }; - const spyBoardRepository = jest - .spyOn(repositoryBoard, 'getBoard') + const spyGetBoardService = jest + .spyOn(getBoardServiceImpl, 'getBoardById') .mockResolvedValue( fakeBoards.find((board) => boardId === board._id) as unknown as ReturnType< - typeof repositoryBoard.getBoard + typeof getBoardServiceImpl.getBoardById > ); @@ -231,7 +267,7 @@ describe('UpdateColumnService', () => { } catch (ex) { expect(ex).toBeInstanceOf(BadRequestException); - expect(spyBoardRepository).toHaveBeenCalledWith(boardId); + expect(spyGetBoardService).toHaveBeenCalledWith(boardId); expect(spyColumnRepository).toHaveBeenCalledTimes(1); } }); diff --git a/backend/src/modules/columns/services/update.column.service.ts b/backend/src/modules/columns/services/update.column.service.ts index 225fa5f11..2c62e2d71 100644 --- a/backend/src/modules/columns/services/update.column.service.ts +++ b/backend/src/modules/columns/services/update.column.service.ts @@ -9,18 +9,18 @@ import { ColumnDeleteCardsDto } from 'src/modules/columns/dto/colum.deleteCards. import { DeleteCardService } from 'src/modules/cards/interfaces/services/delete.card.service.interface'; import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; import { ColumnRepositoryInterface } from '../repositories/column.repository.interface'; -import { BoardRepositoryInterface } from 'src/modules/boards/repositories/board.repository.interface'; +import { GetBoardServiceInterface } from 'src/modules/boards/interfaces/services/get.board.service.interface'; @Injectable() export default class UpdateColumnServiceImpl implements UpdateColumnService { constructor( @Inject(Columns.TYPES.repositories.ColumnRepository) private readonly columnRepository: ColumnRepositoryInterface, - @Inject(Boards.TYPES.repositories.BoardRepository) - private readonly boardRepository: BoardRepositoryInterface, private socketService: SocketGateway, @Inject(Cards.TYPES.services.DeleteCardService) - private deleteCardService: DeleteCardService + private deleteCardService: DeleteCardService, + @Inject(Boards.TYPES.services.GetBoardService) + private getBoardService: GetBoardServiceInterface ) {} async updateColumn(boardId: string, column: UpdateColumnDto) { @@ -36,7 +36,7 @@ export default class UpdateColumnServiceImpl implements UpdateColumnService { } async deleteCardsFromColumn(boardId: string, column: ColumnDeleteCardsDto) { - const board = await this.boardRepository.getBoard(boardId); + const board = await this.getBoardService.getBoardById(boardId); if (!board) { throw new BadRequestException(UPDATE_FAILED); From 091c7b4a37d46fb6063a6025393f5e7377d810a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Wed, 1 Mar 2023 18:17:50 +0000 Subject: [PATCH 04/22] feat: add delete queries to board repository --- .../auth/controller/auth.controller.spec.ts | 2 + .../controller/boards.controller.spec.ts | 2 + .../services/get.board.service.interface.ts | 4 +- .../board-user.repository.interface.ts | 7 ++ .../repositories/board-user.repository.ts | 10 ++- .../board.repository.interface.ts | 15 +++- .../boards/repositories/board.repository.ts | 71 ++++++++++++++-- .../boards/services/delete.board.service.ts | 83 +++++-------------- .../boards/services/get.board.service.spec.ts | 3 +- .../boards/services/get.board.service.ts | 59 ++----------- .../controller/columns.controller.spec.ts | 2 + 11 files changed, 134 insertions(+), 124 deletions(-) diff --git a/backend/src/modules/auth/controller/auth.controller.spec.ts b/backend/src/modules/auth/controller/auth.controller.spec.ts index 33bc16ab4..66fdf0cb1 100644 --- a/backend/src/modules/auth/controller/auth.controller.spec.ts +++ b/backend/src/modules/auth/controller/auth.controller.spec.ts @@ -19,6 +19,7 @@ import { import AuthController from 'src/modules/auth/controller/auth.controller'; import { boardRepository, + boardUserRepository, createBoardService, getBoardApplication, getBoardService @@ -85,6 +86,7 @@ describe('AuthController', () => { createBoardService, createSchedulesService, deleteSchedulesService, + boardUserRepository, SocketGateway, SchedulerRegistry, ConfigService, diff --git a/backend/src/modules/boards/controller/boards.controller.spec.ts b/backend/src/modules/boards/controller/boards.controller.spec.ts index 63841342b..0700d6b5b 100644 --- a/backend/src/modules/boards/controller/boards.controller.spec.ts +++ b/backend/src/modules/boards/controller/boards.controller.spec.ts @@ -4,6 +4,7 @@ import { SchedulerRegistry } from '@nestjs/schedule'; import { Test } from '@nestjs/testing'; import { boardRepository, + boardUserRepository, createBoardApplication, createBoardService, deleteBoardApplication, @@ -56,6 +57,7 @@ describe('BoardsController', () => { createSchedulesService, deleteSchedulesService, teamRepository, + boardUserRepository, teamUserRepository, updateTeamService, getCardService, diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index f176050c9..9cfd3c110 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -36,7 +36,9 @@ export interface GetBoardServiceInterface { countBoards(userId: string): Promise; - getAllBoardIdsAndTeamIdsOfUser(userId: string): Promise<{ boardIds: any[]; teamIds: any[] }>; + getAllBoardIdsAndTeamIdsOfUser( + userId: string + ): Promise<{ boardIds: LeanDocument[]; teamIds: any[] }>; getAllBoardsByTeamId(teamId: string): Promise[]>; diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index e85657bdb..740653ada 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -1,6 +1,13 @@ +import { ObjectId } from 'mongoose'; import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; +import Board from '../entities/board.schema'; import BoardUser from '../entities/board.user.schema'; export interface BoardUserRepositoryInterface extends BaseInterfaceRepository { getAllBoardsIdsOfUser(userId: string): Promise; + deleteManyBoardUsers( + dividedBoards: Board[] | ObjectId[], + withSession: boolean, + boardId: ObjectId | string + ): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index 63e8143cf..1bec951b1 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -1,7 +1,8 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; +import { Model, ObjectId } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; +import Board from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { BoardUserRepositoryInterface } from './board-user.repository.interface'; @@ -16,4 +17,11 @@ export class BoardUserRepository getAllBoardsIdsOfUser(userId: string) { return this.model.find({ user: userId }).select('board').lean().exec(); } + deleteManyBoardUsers( + dividedBoards: Board[] | ObjectId[], + withSession: boolean, + boardId: ObjectId | string + ) { + return this.deleteMany({ board: { $in: [...dividedBoards, boardId] } }, withSession); + } } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 9b5332620..943917cdf 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -1,5 +1,7 @@ +import { ObjectId } from 'mongoose'; import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; import Board from 'src/modules/boards/entities/board.schema'; +import { QueryType } from '../interfaces/findQuery'; export interface BoardRepositoryInterface extends BaseInterfaceRepository { getBoard(boardId: string): Promise; @@ -7,5 +9,16 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository getMainBoard(boardId: string): Promise; getBoardData(boardId: string): Promise; getAllBoardsByTeamId(teamId: string): Promise; - countBoards(boardIds: string[], teamIds: string[]): Promise; + countBoards(boardIds: string[] | ObjectId[], teamIds: string[]): Promise; + getCountPage(query: QueryType): Promise; + getAllBoards( + allBoards: boolean, + query: QueryType, + page: number, + size: number, + count: number + ): Promise; + deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise; + + deleteBoard(boardId: string, withSession: boolean): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index fe8d70278..e39562d29 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; +import { ObjectId } from 'mongoose'; +import { Model, PopulateOptions } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; import { QueryType } from '../interfaces/findQuery'; @@ -42,7 +43,7 @@ export class BoardRepository return this.findAllWithQuery({ team: teamId }, 'board', undefined, false); } - countBoards(boardIds: string[], teamIds: string[]) { + countBoards(boardIds: string[] | ObjectId[], teamIds: string[]) { return this.model .countDocuments({ $and: [ @@ -57,7 +58,67 @@ export class BoardRepository return this.model.find(query).countDocuments().exec(); } - // getAllBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { - // return this.model.find(query).sort(); - // } + getAllBoards(allBoards: boolean, query: QueryType, page: number, size: number, count: number) { + const boardDataToPopulate: PopulateOptions[] = [ + { path: 'createdBy', select: 'firstName lastName' }, + { + path: 'team', + select: 'name users _id', + populate: { + path: 'users', + select: 'user role', + populate: { + path: 'user', + select: '_id firstName lastName joinedAt' + } + } + }, + { + path: 'dividedBoards', + select: + '-__v -createdAt -slackEnable -slackChannelId -submitedAt -id -columns.id -submitedByUser -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments', + populate: [ + { + path: 'users', + select: 'role user', + populate: { + path: 'user', + model: 'User', + select: 'firstName email lastName' + } + } + ] + }, + { + path: 'users', + select: 'user role -board', + populate: { + path: 'user', + select: '_id firstName email lastName isAnonymous' + } + } + ]; + + return this.model + .find(query) + .sort({ updatedAt: 'desc' }) + .skip(allBoards ? 0 : page * size) + .limit(allBoards ? count : size) + .select( + '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments' + ) + .populate(boardDataToPopulate) + .lean({ virtuals: true }) + .exec() as unknown as Promise; + } + + /* Delete Boards */ + + deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise { + return this.deleteMany({ _id: { $in: dividedBoards } }, withSession); + } + + deleteBoard(boardId: string, withSession: boolean) { + return this.findOneAndRemove(boardId, withSession); + } } diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index 1e1648e2b..e5dbb3d1e 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -6,15 +6,11 @@ import { forwardRef } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { ClientSession, LeanDocument, Model, ObjectId } from 'mongoose'; +import { ClientSession, Model, ObjectId } from 'mongoose'; import { DELETE_FAILED } from 'src/libs/exceptions/messages'; import isEmpty from 'src/libs/utils/isEmpty'; import { DeleteSchedulesServiceInterface } from 'src/modules/schedules/interfaces/services/delete.schedules.service.interface'; import * as Schedules from 'src/modules/schedules/interfaces/types'; -import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/get.team.service.interface'; -import * as Teams from 'src/modules/teams/interfaces/types'; -import { TeamUserDocument } from 'src/modules/teams/entities/team.user.schema'; -import { UserDocument } from 'src/modules/users/entities/user.schema'; import { DeleteBoardServiceInterface } from '../interfaces/services/delete.board.service.interface'; import Board, { BoardDocument } from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; @@ -24,6 +20,7 @@ import { GetBoardServiceInterface } from '../interfaces/services/get.board.servi import { ArchiveChannelServiceInterface } from 'src/modules/communication/interfaces/archive-channel.service.interface'; import { ArchiveChannelDataOptions } from 'src/modules/communication/dto/types'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; +import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; @Injectable() export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterface { @@ -32,8 +29,8 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa private boardModel: Model, @Inject(Boards.TYPES.repositories.BoardRepository) private readonly boardRepository: BoardRepositoryInterface, - @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) - private getTeamService: GetTeamServiceInterface, + @Inject(Boards.TYPES.repositories.BoardUserRepository) + private readonly boardUserRepository: BoardUserRepositoryInterface, @Inject(Schedules.TYPES.services.DeleteSchedulesService) private deleteSheduleService: DeleteSchedulesServiceInterface, @InjectModel(BoardUser.name) @@ -44,68 +41,31 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa private archiveChannelService: ArchiveChannelServiceInterface ) {} - private async getTeamUser( - userId: string, - teamId: string - ): Promise> { - const teamUser = await this.getTeamService.getTeamUser(userId, teamId); - - if (!teamUser) { - throw new NotFoundException('User not found on this team!'); - } - - return teamUser; - } - - private async isUserSAdmin( - userId: string, - teamUsers: LeanDocument[] - ): Promise { - const myUser = teamUsers.find( - (user) => String((user.user as UserDocument)?._id) === String(userId) + async deleteSubBoards(dividedBoards: Board[] | ObjectId[], boardSession: boolean) { + const deletedCount = await this.boardRepository.deleteManySubBoards( + dividedBoards, + boardSession ); - const isUserSAdmin = (myUser?.user as UserDocument).isSAdmin; - - return isUserSAdmin; - } - - private async getUsersOfTeam(teamId: string): Promise[]> { - const users = await this.getTeamService.getUsersOfTeam(teamId); - - if (!users) { - throw new NotFoundException('User not found list of users!'); - } - - return users; - } - - async deleteSubBoards(dividedBoards: Board[] | ObjectId[], boardSession: ClientSession) { - const { deletedCount } = await this.boardModel - .deleteMany({ _id: { $in: dividedBoards } }, { session: boardSession }) - .exec(); if (deletedCount !== dividedBoards.length) throw Error(DELETE_FAILED); } async deleteBoardUsers( dividedBoards: Board[] | ObjectId[], - boardSession: ClientSession, - boardId: ObjectId + boardSession: boolean, + boardId: ObjectId | string ) { - const { deletedCount } = await this.boardUserModel - .deleteMany({ board: { $in: [...dividedBoards, boardId] } }, { session: boardSession }) - .exec(); + const deletedCount = await this.boardUserRepository.deleteManyBoardUsers( + dividedBoards, + boardSession, + boardId + ); if (deletedCount <= 0) throw Error(DELETE_FAILED); } - async deleteBoard(boardId: string, boardSession: ClientSession) { - const result = await this.boardModel.findOneAndRemove( - { - _id: boardId - }, - { session: boardSession } - ); + async deleteBoard(boardId: string, boardSession: boolean) { + const result = await this.boardRepository.deleteBoard(boardId, boardSession); if (!result) throw Error(DELETE_FAILED); @@ -150,16 +110,13 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa boardSession.startTransaction(); boardUserSession.startTransaction(); try { - const { _id, dividedBoards, slackEnable } = await this.deleteBoard( - boardId.toString(), - boardSession - ); + const { _id, dividedBoards, slackEnable } = await this.deleteBoard(boardId.toString(), true); this.deleteSheduleService.findAndDeleteScheduleByBoardId(boardId); if (isMainBoard && !isEmpty(dividedBoards)) { - await this.deleteSubBoards(dividedBoards, boardSession); + await this.deleteSubBoards(dividedBoards, true); - await this.deleteBoardUsers(dividedBoards, boardUserSession, _id); + await this.deleteBoardUsers(dividedBoards, true, _id); } else { await this.deleteSimpleBoardUsers(boardUserSession, _id); } diff --git a/backend/src/modules/boards/services/get.board.service.spec.ts b/backend/src/modules/boards/services/get.board.service.spec.ts index f1f85614c..3afb8ad65 100644 --- a/backend/src/modules/boards/services/get.board.service.spec.ts +++ b/backend/src/modules/boards/services/get.board.service.spec.ts @@ -14,7 +14,7 @@ import { updateTeamService } from 'src/modules/teams/providers'; import { userRepository } from 'src/modules/users/users.providers'; -import { boardRepository, getBoardService } from '../boards.providers'; +import { boardRepository, boardUserRepository, getBoardService } from '../boards.providers'; import { cleanBoard } from '../utils/clean-board'; const fakeBoards = BoardFactory.createMany(2); @@ -32,6 +32,7 @@ describe('GetBoardServiceImpl', () => { boardRepository, updateTeamService, userRepository, + boardUserRepository, { provide: getModelToken('Team'), useValue: {} diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 6869287a2..4927f751b 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -7,19 +7,19 @@ import { NotFoundException, forwardRef } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; import { BOARDS_NOT_FOUND, FORBIDDEN, NOT_FOUND } from 'src/libs/exceptions/messages'; import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/get.team.service.interface'; import * as Team from 'src/modules/teams/interfaces/types'; import * as Users from 'src/modules/users/interfaces/types'; import { QueryType } from '../interfaces/findQuery'; import { GetBoardServiceInterface } from '../interfaces/services/get.board.service.interface'; -import Board, { BoardDocument } from '../entities/board.schema'; import { cleanBoard } from '../utils/clean-board'; import { TYPES } from '../interfaces/types'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; +import Board, { BoardDocument } from '../entities/board.schema'; +import { InjectModel } from '@nestjs/mongoose'; +import { Model } from 'mongoose'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { @@ -44,7 +44,7 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { ]); return { - boardIds: boardIds.map((boardUser) => String(boardUser.board)), + boardIds: boardIds.map((boardUser) => boardUser.board), teamIds: teamIds.map((team) => team._id) }; } @@ -106,56 +106,11 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { } async getBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { - const count = await this.boardModel.find(query).countDocuments().exec(); + const count = await this.boardRepository.getCountPage(query); + const hasNextPage = page + 1 < Math.ceil(count / (allBoards ? count : size)); try { - const boards = await this.boardModel - .find(query) - .sort({ updatedAt: 'desc' }) - .skip(allBoards ? 0 : page * size) - .limit(allBoards ? count : size) - .select( - '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments' - ) - .populate({ path: 'createdBy', select: 'firstName lastName' }) - .populate({ - path: 'team', - select: 'name users _id', - populate: { - path: 'users', - select: 'user role', - populate: { - path: 'user', - select: '_id firstName lastName joinedAt' - } - } - }) - .populate({ - path: 'dividedBoards', - select: - '-__v -createdAt -slackEnable -slackChannelId -submitedAt -id -columns.id -submitedByUser -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments', - populate: [ - { - path: 'users', - select: 'role user', - populate: { - path: 'user', - model: 'User', - select: 'firstName email lastName' - } - } - ] - }) - .populate({ - path: 'users', - select: 'user role -board', - populate: { - path: 'user', - select: '_id firstName email lastName isAnonymous' - } - }) - .lean({ virtuals: true }) - .exec(); + const boards = await this.boardRepository.getAllBoards(allBoards, query, page, size, count); return { boards: boards ?? [], hasNextPage, page }; } catch (e) { diff --git a/backend/src/modules/columns/controller/columns.controller.spec.ts b/backend/src/modules/columns/controller/columns.controller.spec.ts index c417a2a38..ae9b2f988 100644 --- a/backend/src/modules/columns/controller/columns.controller.spec.ts +++ b/backend/src/modules/columns/controller/columns.controller.spec.ts @@ -4,6 +4,7 @@ import { SchedulerRegistry } from '@nestjs/schedule'; import { Test } from '@nestjs/testing'; import { boardRepository, + boardUserRepository, createBoardService, getBoardApplication, getBoardService, @@ -64,6 +65,7 @@ describe('ColumnsController', () => { deleteSchedulesService, createBoardService, updateTeamService, + boardUserRepository, { provide: getModelToken('User'), useValue: {} From 1b2fb0a6bdd6161c99de95fb404cd2607484d745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Thu, 2 Mar 2023 11:52:43 +0000 Subject: [PATCH 05/22] fix: fix tests --- .../libs/repositories/mongo/mongo-generic.repository.ts | 8 ++++++-- .../src/modules/boards/repositories/board.repository.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 23d96f88f..96384a077 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -35,8 +35,12 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .exec() as Promise; } - findOneByField( - value: ModelProps | FilterQuery, + findOneByField(value: ModelProps): Promise { + return this._repository.findOne(value).exec(); + } + + findOneByFieldWithQuery( + value: FilterQuery, selectedValues?: SelectedValues, populate?: PopulateType ): Promise { diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index e39562d29..96c69c11f 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -28,7 +28,7 @@ export class BoardRepository } getMainBoard(boardId: string) { - return this.findOneByField({ dividedBoards: { $in: boardId } }, 'title'); + return this.findOneByFieldWithQuery({ dividedBoards: { $in: boardId } }, 'title'); } getBoardData(boardId: string) { From edd62e0815c7adf90e2679dc8eef3d2f914bead9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Thu, 2 Mar 2023 11:59:32 +0000 Subject: [PATCH 06/22] feat: add delete queries to board repositories --- .../board-user.repository.interface.ts | 4 +- .../repositories/board-user.repository.ts | 12 +++++- .../boards/services/delete.board.service.ts | 38 ++++++++----------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index 740653ada..a225eb1c2 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -5,9 +5,11 @@ import BoardUser from '../entities/board.user.schema'; export interface BoardUserRepositoryInterface extends BaseInterfaceRepository { getAllBoardsIdsOfUser(userId: string): Promise; - deleteManyBoardUsers( + deleteDividedBoardUsers( dividedBoards: Board[] | ObjectId[], withSession: boolean, boardId: ObjectId | string ): Promise; + + deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index 1bec951b1..ba7c92803 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -17,11 +17,21 @@ export class BoardUserRepository getAllBoardsIdsOfUser(userId: string) { return this.model.find({ user: userId }).select('board').lean().exec(); } - deleteManyBoardUsers( + + deleteDividedBoardUsers( dividedBoards: Board[] | ObjectId[], withSession: boolean, boardId: ObjectId | string ) { return this.deleteMany({ board: { $in: [...dividedBoards, boardId] } }, withSession); } + + deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean) { + return this.deleteMany( + { + board: boardId + }, + withSession + ); + } } diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index e5dbb3d1e..ff4113e41 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -6,7 +6,7 @@ import { forwardRef } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { ClientSession, Model, ObjectId } from 'mongoose'; +import { Model, ObjectId } from 'mongoose'; import { DELETE_FAILED } from 'src/libs/exceptions/messages'; import isEmpty from 'src/libs/utils/isEmpty'; import { DeleteSchedulesServiceInterface } from 'src/modules/schedules/interfaces/services/delete.schedules.service.interface'; @@ -55,7 +55,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa boardSession: boolean, boardId: ObjectId | string ) { - const deletedCount = await this.boardUserRepository.deleteManyBoardUsers( + const deletedCount = await this.boardUserRepository.deleteDividedBoardUsers( dividedBoards, boardSession, boardId @@ -105,10 +105,8 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa } private async deleteBoardBoardUsersAndSchedules(boardId: string, isMainBoard: boolean) { - const boardSession = await this.boardModel.db.startSession(); - const boardUserSession = await this.boardUserModel.db.startSession(); - boardSession.startTransaction(); - boardUserSession.startTransaction(); + await this.boardRepository.startTransaction(); + await this.boardUserRepository.startTransaction(); try { const { _id, dividedBoards, slackEnable } = await this.deleteBoard(boardId.toString(), true); this.deleteSheduleService.findAndDeleteScheduleByBoardId(boardId); @@ -118,7 +116,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa await this.deleteBoardUsers(dividedBoards, true, _id); } else { - await this.deleteSimpleBoardUsers(boardUserSession, _id); + await this.deleteSimpleBoardUsers(true, _id); } // if slack is enable for the deleted board @@ -142,29 +140,25 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa }); } - await boardSession.commitTransaction(); - await boardUserSession.commitTransaction(); + await this.boardRepository.commitTransaction(); + await this.boardUserRepository.commitTransaction(); return true; } catch (e) { - await boardSession.abortTransaction(); - await boardUserSession.abortTransaction(); + await this.boardRepository.abortTransaction(); + await this.boardUserRepository.abortTransaction(); } finally { - await boardSession.endSession(); - await boardUserSession.endSession(); + await this.boardRepository.endSession(); + await this.boardUserRepository.endSession(); } throw new BadRequestException(DELETE_FAILED); } - private async deleteSimpleBoardUsers(boardSession: ClientSession, boardId: string) { - const { deletedCount } = await this.boardUserModel - .deleteMany( - { - board: boardId - }, - { session: boardSession } - ) - .exec(); + private async deleteSimpleBoardUsers(boardSession: boolean, boardId: string) { + const deletedCount = await this.boardUserRepository.deleteSimpleBoardUsers( + boardId, + boardSession + ); if (deletedCount <= 0) throw Error(DELETE_FAILED); } From a04a39e6107a3c3952a1b070af0e53bbb74f7cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Thu, 2 Mar 2023 13:18:26 +0000 Subject: [PATCH 07/22] feat: add update queries to board repositories --- .../board-user.repository.interface.ts | 6 ++ .../repositories/board-user.repository.ts | 25 ++++++ .../board.repository.interface.ts | 7 +- .../boards/repositories/board.repository.ts | 40 ++++++++- .../boards/services/update.board.service.ts | 83 +++++++------------ 5 files changed, 106 insertions(+), 55 deletions(-) diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index a225eb1c2..f40ef26bc 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -12,4 +12,10 @@ export interface BoardUserRepositoryInterface extends BaseInterfaceRepository; deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean): Promise; + + getBoardResponsible(boardId: string): Promise; + + getVotesCount(boardId: string): Promise; + + updateBoardUserRole(boardId: string, userId: string, role: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index ba7c92803..c4cd790e4 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, ObjectId } from 'mongoose'; +import { BoardRoles } from 'src/libs/enum/board.roles'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; @@ -34,4 +35,28 @@ export class BoardUserRepository withSession ); } + + getBoardResponsible(boardId: string) { + return this.findOneByFieldWithQuery( + { board: boardId, role: BoardRoles.RESPONSIBLE }, + {}, + { path: 'user' } + ); + } + + getVotesCount(boardId: string) { + return this.model.find({ board: boardId }, ['votesCount']).exec(); + } + + updateBoardUserRole(boardId: string, userId: string, role: string) { + return this.findOneByFieldAndUpdate( + { + user: userId, + board: boardId + }, + { + role: role + } + ); + } } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 943917cdf..f85ac1064 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -1,4 +1,4 @@ -import { ObjectId } from 'mongoose'; +import { FilterQuery, ObjectId } from 'mongoose'; import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; import Board from 'src/modules/boards/entities/board.schema'; import { QueryType } from '../interfaces/findQuery'; @@ -18,7 +18,10 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository size: number, count: number ): Promise; + getResponsiblesSlackId(boardId: string): Promise; + getBoardByQuery(query: FilterQuery): Promise; deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise; - deleteBoard(boardId: string, withSession: boolean): Promise; + updateBoard(boardId: string, board: Board, isNew: boolean): Promise; + updateMergedSubBoard(subBoardId: string, userId: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 96c69c11f..3465021f0 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; -import { ObjectId } from 'mongoose'; +import { FilterQuery, ObjectId } from 'mongoose'; import { Model, PopulateOptions } from 'mongoose'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; @@ -31,6 +31,10 @@ export class BoardRepository return this.findOneByFieldWithQuery({ dividedBoards: { $in: boardId } }, 'title'); } + getBoardByQuery(query: FilterQuery) { + return this.findOneByFieldWithQuery(query); + } + getBoardData(boardId: string) { return this.findOneById( boardId, @@ -112,6 +116,10 @@ export class BoardRepository .exec() as unknown as Promise; } + getResponsiblesSlackId(boardId: string) { + return this.findOneByFieldWithQuery({ dividedBoards: { $in: [boardId] } }, 'slackChannelId'); + } + /* Delete Boards */ deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise { @@ -121,4 +129,34 @@ export class BoardRepository deleteBoard(boardId: string, withSession: boolean) { return this.findOneAndRemove(boardId, withSession); } + + /* Update Boards */ + + updateBoard(boardId: string, board: Board, isNew: boolean) { + return this.findOneByFieldAndUpdate( + { + _id: boardId + }, + { + ...board + }, + { + new: isNew + } + ); + } + + updateMergedSubBoard(subBoardId: string, userId: string) { + return this.findOneByFieldAndUpdate( + { + _id: subBoardId + }, + { + $set: { + submitedByUser: userId, + submitedAt: new Date() + } + } + ); + } } diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index 9279114dc..165507d50 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -8,7 +8,6 @@ import { } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, ObjectId } from 'mongoose'; -import { BoardRoles } from 'src/libs/enum/board.roles'; import { getIdFromObjectId } from 'src/libs/utils/getIdFromObjectId'; import isEmpty from 'src/libs/utils/isEmpty'; import { TeamDto } from 'src/modules/communication/dto/team.dto'; @@ -18,7 +17,7 @@ import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/g import * as Teams from 'src/modules/teams/interfaces/types'; import * as Cards from 'src/modules/cards/interfaces/types'; import * as Boards from '../interfaces/types'; -import User, { UserDocument } from 'src/modules/users/entities/user.schema'; +import User from 'src/modules/users/entities/user.schema'; import { UpdateBoardDto } from '../dto/update-board.dto'; import { ResponsibleType } from '../interfaces/responsible.interface'; import { UpdateBoardServiceInterface } from '../interfaces/services/update.board.service.interface'; @@ -34,6 +33,7 @@ import { BOARD_PHASE_SERVER_UPDATED } from 'src/libs/constants/phase'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { BoardPhaseDto } from 'src/libs/dto/board-phase.dto'; import PhaseChangeEvent from 'src/modules/socket/events/user-updated-phase.event'; +import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; @Injectable() export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterface { @@ -50,6 +50,8 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa private deleteCardService: DeleteCardService, @Inject(Boards.TYPES.repositories.BoardRepository) private readonly boardRepository: BoardRepositoryInterface, + @Inject(Boards.TYPES.repositories.BoardUserRepository) + private readonly boardUserRepository: BoardUserRepositoryInterface, private eventEmitter: EventEmitter2 ) {} @@ -60,16 +62,13 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa * @private */ private async getBoardResponsibleInfo(boardId: string): Promise { - const boardUser = await this.boardUserModel - .findOne({ board: boardId, role: BoardRoles.RESPONSIBLE }) - .populate({ path: 'user' }) - .exec(); + const boardUser = await this.boardUserRepository.getBoardResponsible(boardId); if (!boardUser) { return undefined; } - const user = boardUser?.user as UserDocument; + const user = boardUser?.user as User; return { id: user._id, email: user.email }; } @@ -80,7 +79,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa * @return number */ private async getHighestVotesOnBoard(boardId: string): Promise { - const votesCount = await this.boardUserModel.find({ board: boardId }, ['votesCount']); + const votesCount = await this.boardUserRepository.getVotesCount(boardId); return votesCount.reduce( (prev, current) => (current.votesCount > prev ? current.votesCount : prev), @@ -121,17 +120,11 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa .map((boardUser) => { const typedBoardUser = boardUser.user as unknown as User; - return this.boardUserModel - .findOneAndUpdate( - { - user: typedBoardUser._id, - board: boardId - }, - { - role: boardUser.role - } - ) - .exec(); + return this.boardUserRepository.updateBoardUserRole( + boardId, + typedBoardUser._id, + boardUser.role + ); }); await Promise.all(promises); } @@ -208,20 +201,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa } } - const updatedBoard = await this.boardModel - .findOneAndUpdate( - { - _id: boardId - }, - { - ...board - }, - { - new: true - } - ) - .lean() - .exec(); + const updatedBoard = await this.boardRepository.updateBoard(boardId, board, true); if (!updatedBoard) throw new BadRequestException(UPDATE_FAILED); @@ -307,7 +287,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa newResponsibleEmail: newResponsible.email, previousResponsibleEmail: currentResponsible?.email ?? '', subTeamChannelId: slackChannelId, - responsiblesChannelId: (await this.boardModel.findOne({ dividedBoards: { $in: [boardId] } })) + responsiblesChannelId: (await this.boardRepository.getResponsiblesSlackId(boardId)) ?.slackChannelId, teamNumber: boardNumber, email: newResponsible.email @@ -317,10 +297,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa async mergeBoards(subBoardId: string, userId: string) { const [subBoard, board] = await Promise.all([ this.boardRepository.getBoard(subBoardId), - this.boardModel - .findOne({ dividedBoards: { $in: [subBoardId] } }) - .lean() - .exec() + this.boardRepository.getBoardByQuery({ dividedBoards: { $in: [subBoardId] } }) ]); if (!subBoard || !board || subBoard.submitedByUser) return null; @@ -335,20 +312,22 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa newColumns[i].cards = [...newColumns[i].cards, ...newSubColumns[i].cards]; } - this.boardModel - .findOneAndUpdate( - { - _id: subBoardId - }, - { - $set: { - submitedByUser: userId, - submitedAt: new Date() - } - } - ) - .lean() - .exec(); + await this.boardRepository.updateMergedSubBoard(subBoardId, userId); + + // this.boardModel + // .findOneAndUpdate( + // { + // _id: subBoardId + // }, + // { + // $set: { + // submitedByUser: userId, + // submitedAt: new Date() + // } + // } + // ) + // .lean() + // .exec(); const result = this.boardModel .findOneAndUpdate( From 5a496c2a778bf7531c4dd99bd92a73470dca9f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Thu, 2 Mar 2023 17:46:33 +0000 Subject: [PATCH 08/22] feat: add update queries to board repositories --- .../interfaces/base.repository.interface.ts | 2 + .../mongo/mongo-generic.repository.ts | 6 +- .../boards/entities/board.user.schema.ts | 3 +- .../services/get.board.service.interface.ts | 3 +- .../board-user.repository.interface.ts | 14 +-- .../repositories/board-user.repository.ts | 53 +++++++---- .../board.repository.interface.ts | 12 ++- .../boards/repositories/board.repository.ts | 77 ++++++++++------ .../boards/services/create.board.service.ts | 6 +- .../boards/services/delete.board.service.ts | 3 +- .../boards/services/get.board.service.ts | 5 +- .../boards/services/update.board.service.ts | 91 +++++-------------- .../services/create.schedules.service.ts | 3 +- backend/src/modules/teams/interfaces/types.ts | 1 - .../ParticipantCard.tsx/index.tsx | 1 + 15 files changed, 150 insertions(+), 130 deletions(-) diff --git a/backend/src/libs/repositories/interfaces/base.repository.interface.ts b/backend/src/libs/repositories/interfaces/base.repository.interface.ts index cd2eb8a9e..dc3534564 100644 --- a/backend/src/libs/repositories/interfaces/base.repository.interface.ts +++ b/backend/src/libs/repositories/interfaces/base.repository.interface.ts @@ -28,6 +28,8 @@ export interface BaseInterfaceRepository { countDocuments(): Promise; + countDocumentsWithQuery(filter: FilterQuery, options?: QueryOptions): Promise; + findOneByFieldAndUpdate( value: FilterQuery, query: UpdateQuery, diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 96384a077..5fe42bd37 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -18,6 +18,10 @@ export class MongoGenericRepository implements BaseInterfaceRepository { return this._repository.countDocuments().lean().exec(); } + countDocumentsWithQuery(filter: FilterQuery, options?: QueryOptions): Promise { + return this._repository.countDocuments(filter, options).lean().exec(); + } + findAll( selectedValues?: SelectedValues, sort?: SortType, @@ -69,7 +73,7 @@ export class MongoGenericRepository implements BaseInterfaceRepository { return this._repository.create(item); } - insertMany(listOfItems: T[]): Promise { + insertMany(listOfItems: Q[]): Promise { return this._repository.insertMany(listOfItems); } diff --git a/backend/src/modules/boards/entities/board.user.schema.ts b/backend/src/modules/boards/entities/board.user.schema.ts index 1a44ff9c9..6b9665797 100644 --- a/backend/src/modules/boards/entities/board.user.schema.ts +++ b/backend/src/modules/boards/entities/board.user.schema.ts @@ -1,6 +1,7 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document, ObjectId, SchemaTypes } from 'mongoose'; import { BoardRoles } from 'src/libs/enum/board.roles'; +import BaseModel from 'src/libs/models/base.model'; import User from 'src/modules/users/entities/user.schema'; export type BoardUserDocument = BoardUser & Document; @@ -10,7 +11,7 @@ export type BoardUserDocument = BoardUser & Document; virtuals: true } }) -export default class BoardUser { +export default class BoardUser extends BaseModel { @Prop({ nullable: false, type: String, diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 9cfd3c110..134cfd122 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -1,4 +1,5 @@ import { LeanDocument } from 'mongoose'; +import { PopulateType } from 'src/libs/repositories/interfaces/base.repository.interface'; import Board, { BoardDocument } from '../../entities/board.schema'; import { BoardsAndPage } from '../boards-page.interface'; @@ -42,7 +43,7 @@ export interface GetBoardServiceInterface { getAllBoardsByTeamId(teamId: string): Promise[]>; - getBoardPopulated(boardId: string): Promise; + getBoardPopulated(boardId: string, populate?: PopulateType): Promise; isBoardPublic(boardId: string): Promise; diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index f40ef26bc..7c6890b04 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -1,21 +1,21 @@ import { ObjectId } from 'mongoose'; import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; +import BoardUserDto from '../dto/board.user.dto'; import Board from '../entities/board.schema'; import BoardUser from '../entities/board.user.schema'; export interface BoardUserRepositoryInterface extends BaseInterfaceRepository { getAllBoardsIdsOfUser(userId: string): Promise; + + getBoardResponsible(boardId: string): Promise; + getVotesCount(boardId: string): Promise; + createBoardUsers(boardUsers: BoardUserDto[]): Promise; + updateBoardUserRole(boardId: string, userId: string, role: string): Promise; deleteDividedBoardUsers( dividedBoards: Board[] | ObjectId[], withSession: boolean, boardId: ObjectId | string ): Promise; - deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean): Promise; - - getBoardResponsible(boardId: string): Promise; - - getVotesCount(boardId: string): Promise; - - updateBoardUserRole(boardId: string, userId: string, role: string): Promise; + deleteBoardUsers(boardUsers: string[]): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index c4cd790e4..c5b60cdf7 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -3,6 +3,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model, ObjectId } from 'mongoose'; import { BoardRoles } from 'src/libs/enum/board.roles'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; +import BoardUserDto from '../dto/board.user.dto'; import Board from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { BoardUserRepositoryInterface } from './board-user.repository.interface'; @@ -15,25 +16,10 @@ export class BoardUserRepository constructor(@InjectModel(BoardUser.name) private model: Model) { super(model); } - getAllBoardsIdsOfUser(userId: string) { - return this.model.find({ user: userId }).select('board').lean().exec(); - } - - deleteDividedBoardUsers( - dividedBoards: Board[] | ObjectId[], - withSession: boolean, - boardId: ObjectId | string - ) { - return this.deleteMany({ board: { $in: [...dividedBoards, boardId] } }, withSession); - } - deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean) { - return this.deleteMany( - { - board: boardId - }, - withSession - ); + /* GET BOARD USERS */ + getAllBoardsIdsOfUser(userId: string) { + return this.findAllWithQuery({ user: userId }, 'board'); } getBoardResponsible(boardId: string) { @@ -48,7 +34,12 @@ export class BoardUserRepository return this.model.find({ board: boardId }, ['votesCount']).exec(); } - updateBoardUserRole(boardId: string, userId: string, role: string) { + createBoardUsers(boardUsers: BoardUserDto[]) { + return this.insertMany(boardUsers); + } + + /* UPDATE BOARD USERS */ + updateBoardUserRole(boardId: string, userId: string, role: BoardRoles) { return this.findOneByFieldAndUpdate( { user: userId, @@ -59,4 +50,28 @@ export class BoardUserRepository } ); } + + /* DELETE BOARD USERS */ + deleteDividedBoardUsers( + dividedBoards: Board[] | ObjectId[], + withSession: boolean, + boardId: ObjectId | string + ) { + return this.deleteMany({ board: { $in: [...dividedBoards, boardId] } }, withSession); + } + + deleteSimpleBoardUsers(boardId: ObjectId | string, withSession: boolean) { + return this.deleteMany( + { + board: boardId + }, + withSession + ); + } + + deleteBoardUsers(boardUsers: string[]) { + return this.deleteMany({ + _id: { $in: boardUsers } + }); + } } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index f85ac1064..94ff7bf81 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -1,11 +1,16 @@ import { FilterQuery, ObjectId } from 'mongoose'; -import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; +import { BoardPhases } from 'src/libs/enum/board.phases'; +import { + BaseInterfaceRepository, + PopulateType +} from 'src/libs/repositories/interfaces/base.repository.interface'; import Board from 'src/modules/boards/entities/board.schema'; +import Column from 'src/modules/columns/entities/column.schema'; import { QueryType } from '../interfaces/findQuery'; export interface BoardRepositoryInterface extends BaseInterfaceRepository { getBoard(boardId: string): Promise; - getBoardPopulated(boardId: string): Promise; + getBoardPopulated(boardId: string, populate?: PopulateType): Promise; getMainBoard(boardId: string): Promise; getBoardData(boardId: string): Promise; getAllBoardsByTeamId(teamId: string): Promise; @@ -24,4 +29,7 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository deleteBoard(boardId: string, withSession: boolean): Promise; updateBoard(boardId: string, board: Board, isNew: boolean): Promise; updateMergedSubBoard(subBoardId: string, userId: string): Promise; + updateMergedBoard(boardId: string, newColumns: Column[]): Promise; + updatedChannelId(boardId: string, channelId: string): Promise; + updateBoardPhase(boardId: string, phase: BoardPhases): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 3465021f0..cac910b1c 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -2,10 +2,13 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { FilterQuery, ObjectId } from 'mongoose'; import { Model, PopulateOptions } from 'mongoose'; +import { BoardPhases } from 'src/libs/enum/board.phases'; +import { PopulateType } from 'src/libs/repositories/interfaces/base.repository.interface'; import { MongoGenericRepository } from 'src/libs/repositories/mongo/mongo-generic.repository'; import Board, { BoardDocument } from 'src/modules/boards/entities/board.schema'; +import Column from 'src/modules/columns/entities/column.schema'; import { QueryType } from '../interfaces/findQuery'; -import { BoardDataPopulate, GetBoardDataPopulate } from '../utils/populate-board'; +import { GetBoardDataPopulate } from '../utils/populate-board'; import { BoardRepositoryInterface } from './board.repository.interface'; @Injectable() @@ -17,14 +20,13 @@ export class BoardRepository super(model); } - /* Get Boards */ - + /* GET BOARD */ getBoard(boardId: string): Promise { return this.findOneById(boardId); } - getBoardPopulated(boardId: string) { - return this.findOneById(boardId, {}, BoardDataPopulate); + getBoardPopulated(boardId: string, populate?: PopulateType) { + return this.findOneById(boardId, {}, populate); } getMainBoard(boardId: string) { @@ -47,17 +49,6 @@ export class BoardRepository return this.findAllWithQuery({ team: teamId }, 'board', undefined, false); } - countBoards(boardIds: string[] | ObjectId[], teamIds: string[]) { - return this.model - .countDocuments({ - $and: [ - { isSubBoard: false }, - { $or: [{ _id: { $in: boardIds } }, { team: { $in: teamIds } }] } - ] - }) - .exec(); - } - getCountPage(query: QueryType) { return this.model.find(query).countDocuments().exec(); } @@ -120,18 +111,16 @@ export class BoardRepository return this.findOneByFieldWithQuery({ dividedBoards: { $in: [boardId] } }, 'slackChannelId'); } - /* Delete Boards */ - - deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise { - return this.deleteMany({ _id: { $in: dividedBoards } }, withSession); - } - - deleteBoard(boardId: string, withSession: boolean) { - return this.findOneAndRemove(boardId, withSession); + countBoards(boardIds: string[] | ObjectId[], teamIds: string[]) { + return this.countDocumentsWithQuery({ + $and: [ + { isSubBoard: false }, + { $or: [{ _id: { $in: boardIds } }, { team: { $in: teamIds } }] } + ] + }); } - /* Update Boards */ - + /* UPDATE BOARDS */ updateBoard(boardId: string, board: Board, isNew: boolean) { return this.findOneByFieldAndUpdate( { @@ -159,4 +148,40 @@ export class BoardRepository } ); } + + updateMergedBoard(boardId: string, newColumns: Column[]) { + return this.findOneByFieldAndUpdate( + { + _id: boardId + }, + { + $set: { columns: newColumns } + }, + { new: true } + ); + } + + updatedChannelId(boardId: string, channelId: string) { + return this.findOneByFieldAndUpdate({ _id: boardId }, { slackChannelId: channelId }); + } + + updateBoardPhase(boardId: string, phase: BoardPhases) { + return this.findOneByFieldAndUpdate( + { + _id: boardId + }, + { + phase + } + ); + } + + /* DELETE BOARD */ + deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise { + return this.deleteMany({ _id: { $in: dividedBoards } }, withSession); + } + + deleteBoard(boardId: string, withSession: boolean) { + return this.findOneAndRemove(boardId, withSession); + } } diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index ca5093a89..f016b053a 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -30,6 +30,7 @@ import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { UpdateTeamServiceInterface } from 'src/modules/teams/interfaces/services/update.team.service.interface'; import { addDays, addMonths, isAfter } from 'date-fns'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; +import { BoardDataPopulate } from '../utils/populate-board'; export interface CreateBoardDto { maxUsers: number; @@ -186,7 +187,10 @@ export default class CreateBoardServiceImpl implements CreateBoardService { this.logger.verbose(`Communication Slack Enable is set to "${boardData.slackEnable}".`); if (slackEnable && team && teamData.name === 'xgeeks') { - const populatedBoard = await this.getBoardService.getBoardPopulated(newBoard._id); + const populatedBoard = await this.getBoardService.getBoardPopulated( + newBoard._id, + BoardDataPopulate + ); if (populatedBoard) { this.logger.verbose(`Call Slack Communication Service for board id "${newBoard._id}".`); diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index ff4113e41..a85864e28 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -21,6 +21,7 @@ import { ArchiveChannelServiceInterface } from 'src/modules/communication/interf import { ArchiveChannelDataOptions } from 'src/modules/communication/dto/types'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; +import { BoardDataPopulate } from '../utils/populate-board'; @Injectable() export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterface { @@ -124,7 +125,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa // archive all related channels // for that we need to fetch the board with all dividedBoards - const board = await this.getBoardService.getBoardPopulated(boardId); + const board = await this.getBoardService.getBoardPopulated(boardId, BoardDataPopulate); this.archiveChannelService.execute({ type: ArchiveChannelDataOptions.BOARD, diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 4927f751b..fb166d01c 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -20,6 +20,7 @@ import { BoardRepositoryInterface } from '../repositories/board.repository.inter import Board, { BoardDocument } from '../entities/board.schema'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; +import { PopulateType } from 'src/libs/repositories/interfaces/base.repository.interface'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { @@ -152,8 +153,8 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return this.boardRepository.getAllBoardsByTeamId(teamId); } - getBoardPopulated(boardId: string) { - return this.boardRepository.getBoardPopulated(boardId); + getBoardPopulated(boardId: string, populate?: PopulateType) { + return this.boardRepository.getBoardPopulated(boardId, populate); } getBoardById(boardId: string) { diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index 165507d50..bc82de641 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -147,17 +147,11 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa .map((boardUser) => { const typedBoardUser = boardUser.user as unknown as User; - return this.boardUserModel - .findOneAndUpdate( - { - user: typedBoardUser._id, - board: mainBoardId - }, - { - role: boardUser.role - } - ) - .exec(); + return this.boardUserRepository.updateBoardUserRole( + mainBoardId._id, + typedBoardUser._id, + boardUser.role + ); }); await Promise.all(promises); } @@ -314,33 +308,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa await this.boardRepository.updateMergedSubBoard(subBoardId, userId); - // this.boardModel - // .findOneAndUpdate( - // { - // _id: subBoardId - // }, - // { - // $set: { - // submitedByUser: userId, - // submitedAt: new Date() - // } - // } - // ) - // .lean() - // .exec(); - - const result = this.boardModel - .findOneAndUpdate( - { - _id: board._id - }, - { - $set: { columns: newColumns } - }, - { new: true } - ) - .lean() - .exec(); + const result = await this.boardRepository.updateMergedBoard(board._id, newColumns); if (board.slackChannelId && board.slackEnable) { this.slackCommunicationService.executeMergeBoardNotification({ @@ -356,7 +324,9 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa } private async checkIfIsLastBoardToMerge(mainBoardId: string): Promise { - const board = await this.boardModel.findById(mainBoardId).populate({ path: 'dividedBoards' }); + const board = await this.boardRepository.getBoardPopulated(mainBoardId, { + path: 'dividedBoards' + }); if (!board) return false; @@ -422,25 +392,22 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa updateChannelId(teams: TeamDto[]) { Promise.all( - teams.map((team) => - this.boardModel.updateOne({ _id: team.boardId }, { slackChannelId: team.channelId }) - ) + teams.map((team) => this.boardRepository.updatedChannelId(team.boardId, team.channelId)) ); } async updateBoardParticipantsRole(boardUserToUpdateRole: BoardUserDto) { - const updatedBoardUsers = await this.boardUserModel - .findOneAndUpdate( - { - _id: boardUserToUpdateRole._id - }, - { - role: boardUserToUpdateRole.role - } - ) - .exec(); + const user = boardUserToUpdateRole.user as unknown as User; - if (!updatedBoardUsers) throw new BadRequestException(UPDATE_FAILED); + const updatedBoardUsers = await this.boardUserRepository.updateBoardUserRole( + boardUserToUpdateRole.board, + user._id, + boardUserToUpdateRole.role + ); + + if (!updatedBoardUsers) { + throw new BadRequestException(UPDATE_FAILED); + } return updatedBoardUsers; } @@ -462,16 +429,8 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa async updatePhase(boardPhaseDto: BoardPhaseDto) { try { const { boardId, phase } = boardPhaseDto; - await this.boardModel - .findOneAndUpdate( - { - _id: boardId - }, - { - phase - } - ) - .exec(); + + await this.boardRepository.updateBoardPhase(boardId, phase); this.eventEmitter.emit(BOARD_PHASE_SERVER_UPDATED, new PhaseChangeEvent(boardPhaseDto)); } catch (err) { @@ -480,7 +439,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa } private async addBoardUsers(boardUsers: BoardUserDto[]) { - const createdBoardUsers = await this.boardUserModel.insertMany(boardUsers); + const createdBoardUsers = await this.boardUserRepository.createBoardUsers(boardUsers); if (createdBoardUsers.length < 1) throw new Error(INSERT_FAILED); @@ -488,9 +447,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa } private async deleteBoardUsers(boardUsers: string[]) { - const { deletedCount } = await this.boardUserModel.deleteMany({ - _id: { $in: boardUsers } - }); + const deletedCount = await this.boardUserRepository.deleteBoardUsers(boardUsers); if (deletedCount <= 0) throw new Error(DELETE_FAILED); } diff --git a/backend/src/modules/schedules/services/create.schedules.service.ts b/backend/src/modules/schedules/services/create.schedules.service.ts index 5d0d3f5d1..8477c7ab6 100644 --- a/backend/src/modules/schedules/services/create.schedules.service.ts +++ b/backend/src/modules/schedules/services/create.schedules.service.ts @@ -23,6 +23,7 @@ import { DeleteSchedulesServiceInterface } from '../interfaces/services/delete.s import { TYPES } from '../interfaces/types'; import Schedules, { SchedulesDocument } from '../schemas/schedules.schema'; import { BoardRepositoryInterface } from 'src/modules/boards/repositories/board.repository.interface'; +import { BoardDataPopulate } from 'src/modules/boards/utils/populate-board'; @Injectable() export class CreateSchedulesService implements CreateSchedulesServiceInterface { @@ -114,7 +115,7 @@ export class CreateSchedulesService implements CreateSchedulesServiceInterface { oldBoardId ); - const oldBoard = await this.boardRepository.getBoardPopulated(oldBoardId); + const oldBoard = await this.boardRepository.getBoardPopulated(oldBoardId, BoardDataPopulate); if (!oldBoard) { await this.deleteSchedulesService.deleteScheduleByBoardId(oldBoardId); diff --git a/backend/src/modules/teams/interfaces/types.ts b/backend/src/modules/teams/interfaces/types.ts index bfc613754..119e4bf9e 100644 --- a/backend/src/modules/teams/interfaces/types.ts +++ b/backend/src/modules/teams/interfaces/types.ts @@ -12,7 +12,6 @@ export const TYPES = { UpdateTeamApplication: 'UpdateTeamApplication', DeleteTeamApplication: 'DeleteTeamApplication', DeleteTeamUserApplication: 'DeleteTeamUserApplication' - // UpdateBoardApplication: 'UpdateBoardApplication', }, repositories: { TeamRepository: 'TeamRepository', diff --git a/frontend/src/components/Board/RegularBoard/ParticipantsList/ParticipantCard.tsx/index.tsx b/frontend/src/components/Board/RegularBoard/ParticipantsList/ParticipantCard.tsx/index.tsx index 9616c77ff..f72b58670 100644 --- a/frontend/src/components/Board/RegularBoard/ParticipantsList/ParticipantCard.tsx/index.tsx +++ b/frontend/src/components/Board/RegularBoard/ParticipantsList/ParticipantCard.tsx/index.tsx @@ -63,6 +63,7 @@ const ParticipantCard = React.memo( boardUserToUpdateRole: { ...member, role: checked ? BoardUserRoles.RESPONSIBLE : BoardUserRoles.MEMBER, + board: boardId, }, boardId, }; From 044aaa748d8bd42e3f8352b1d78b12b14a14c3d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Thu, 2 Mar 2023 21:43:47 +0000 Subject: [PATCH 09/22] feat: add create board queries to board repository --- .../applications/create.board.application.ts | 4 +-- backend/src/modules/boards/dto/board.dto.ts | 2 +- .../src/modules/boards/dto/board.user.dto.ts | 3 +- .../modules/boards/entities/board.schema.ts | 30 +++++++++---------- .../boards/entities/board.user.schema.ts | 4 +-- .../create.board.application.interface.ts | 4 +-- .../create.board.service.interface.ts | 8 ++--- .../board-user.repository.interface.ts | 2 +- .../repositories/board-user.repository.ts | 2 +- .../board.repository.interface.ts | 5 +++- .../boards/repositories/board.repository.ts | 5 +++- .../boards/services/create.board.service.ts | 20 ++++++++----- .../boards/services/delete.board.service.ts | 6 ++-- backend/src/modules/cards/dto/card.dto.ts | 17 ++++++++++- .../src/modules/cards/dto/card.item.dto.ts | 16 ++++++++-- backend/src/modules/columns/dto/column.dto.ts | 8 ++--- .../modules/columns/entities/column.schema.ts | 6 ++-- .../src/modules/comments/dto/comment.dto.ts | 10 +++---- 18 files changed, 94 insertions(+), 58 deletions(-) diff --git a/backend/src/modules/boards/applications/create.board.application.ts b/backend/src/modules/boards/applications/create.board.application.ts index 7e650dd5d..c8312cef0 100644 --- a/backend/src/modules/boards/applications/create.board.application.ts +++ b/backend/src/modules/boards/applications/create.board.application.ts @@ -3,7 +3,7 @@ import BoardDto from '../dto/board.dto'; import { CreateBoardApplicationInterface } from '../interfaces/applications/create.board.application.interface'; import { CreateBoardService } from '../interfaces/services/create.board.service.interface'; import { TYPES } from '../interfaces/types'; -import { BoardDocument } from '../entities/board.schema'; +import Board from '../entities/board.schema'; @Injectable() export class CreateBoardApplication implements CreateBoardApplicationInterface { @@ -12,7 +12,7 @@ export class CreateBoardApplication implements CreateBoardApplicationInterface { private createBoardService: CreateBoardService ) {} - create(board: BoardDto, userId: string): Promise { + create(board: BoardDto, userId: string): Promise { return this.createBoardService.create(board, userId); } } diff --git a/backend/src/modules/boards/dto/board.dto.ts b/backend/src/modules/boards/dto/board.dto.ts index 1d554b30d..7ff297e6f 100644 --- a/backend/src/modules/boards/dto/board.dto.ts +++ b/backend/src/modules/boards/dto/board.dto.ts @@ -75,7 +75,7 @@ export default class BoardDto { @ValidateNested({ each: true }) @Type(() => BoardDto) @IsOptional() - dividedBoards!: BoardDto[]; + dividedBoards!: BoardDto[] | string[]; @ApiPropertyOptional({ type: String }) @IsOptional() diff --git a/backend/src/modules/boards/dto/board.user.dto.ts b/backend/src/modules/boards/dto/board.user.dto.ts index 78bd1e7ba..110ad47c4 100644 --- a/backend/src/modules/boards/dto/board.user.dto.ts +++ b/backend/src/modules/boards/dto/board.user.dto.ts @@ -35,9 +35,8 @@ export default class BoardUserDto { board?: string; @ApiPropertyOptional({ default: 0 }) - @IsOptional() @IsNumber() - votesCount?: number; + votesCount: number; @ApiPropertyOptional() @IsOptional() diff --git a/backend/src/modules/boards/entities/board.schema.ts b/backend/src/modules/boards/entities/board.schema.ts index 354687b69..f22843bd7 100644 --- a/backend/src/modules/boards/entities/board.schema.ts +++ b/backend/src/modules/boards/entities/board.schema.ts @@ -22,17 +22,17 @@ export default class Board extends BaseModel { @Prop({ nullable: false }) isPublic!: boolean; - @Prop({ type: SchemaTypes.ObjectId, ref: 'User' }) - submitedByUser!: ObjectId; + @Prop({ type: SchemaTypes.ObjectId, ref: 'User', nullable: true, default: null }) + submitedByUser?: ObjectId | string; @Prop({ type: Date, nullable: true, default: null }) - submitedAt!: Date; + submitedAt?: Date; @Prop({ nullable: false, type: [ColumnSchema] }) columns!: Column[]; - @Prop({ type: [{ type: SchemaTypes.ObjectId, ref: 'Board' }] }) - dividedBoards!: Board[] | ObjectId[]; + @Prop({ type: [{ type: SchemaTypes.ObjectId, ref: 'Board' }], nullable: true }) + dividedBoards?: Board[] | ObjectId[] | string[]; @Prop({ type: SchemaTypes.ObjectId, @@ -40,19 +40,19 @@ export default class Board extends BaseModel { nullable: true, default: null }) - team!: Team | ObjectId; + team?: Team | ObjectId | string; @Prop({ type: SchemaTypes.ObjectId, ref: 'User' }) - createdBy!: User | ObjectId; + createdBy!: User | ObjectId | string; - @Prop({ type: Boolean, default: false }) - recurrent!: boolean; + @Prop({ type: Boolean, nullable: true, default: false }) + recurrent?: boolean; @Prop({ type: Boolean, default: false }) isSubBoard!: boolean; - @Prop({ type: Number, default: 0 }) - boardNumber!: number; + @Prop({ type: Number, nullable: true, default: 0 }) + boardNumber?: number; @Prop({ type: Number, nullable: true, default: null }) maxVotes?: number; @@ -63,17 +63,17 @@ export default class Board extends BaseModel { @Prop({ type: Boolean, nullable: false, default: false }) hideVotes?: boolean; - @Prop({ type: Boolean, nullable: false, default: false }) - slackEnable!: boolean; + @Prop({ type: Boolean, nullable: true, default: false }) + slackEnable?: boolean; @Prop({ type: String, nullable: true, default: null }) slackChannelId?: string; @Prop({ type: Boolean, nullable: true, default: true }) - addCards: boolean; + addCards?: boolean; @Prop({ type: Boolean, nullable: true, default: false }) - postAnonymously: boolean; + postAnonymously?: boolean; @Prop({ type: String, diff --git a/backend/src/modules/boards/entities/board.user.schema.ts b/backend/src/modules/boards/entities/board.user.schema.ts index 6b9665797..6001961a3 100644 --- a/backend/src/modules/boards/entities/board.user.schema.ts +++ b/backend/src/modules/boards/entities/board.user.schema.ts @@ -20,10 +20,10 @@ export default class BoardUser extends BaseModel { role!: string; @Prop({ type: SchemaTypes.ObjectId, ref: 'User', nullable: false }) - user!: User | ObjectId; + user!: User | ObjectId | string; @Prop({ type: SchemaTypes.ObjectId, ref: 'Board', nullable: false }) - board!: ObjectId; + board!: ObjectId | string; @Prop({ nullable: false }) votesCount!: number; diff --git a/backend/src/modules/boards/interfaces/applications/create.board.application.interface.ts b/backend/src/modules/boards/interfaces/applications/create.board.application.interface.ts index 482a41000..474b62964 100644 --- a/backend/src/modules/boards/interfaces/applications/create.board.application.interface.ts +++ b/backend/src/modules/boards/interfaces/applications/create.board.application.interface.ts @@ -1,6 +1,6 @@ import BoardDto from '../../dto/board.dto'; -import { BoardDocument } from '../../entities/board.schema'; +import Board from '../../entities/board.schema'; export interface CreateBoardApplicationInterface { - create(boardData: BoardDto, userId: string): Promise; + create(boardData: BoardDto, userId: string): Promise; } diff --git a/backend/src/modules/boards/interfaces/services/create.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/create.board.service.interface.ts index 92939968e..f6be92777 100644 --- a/backend/src/modules/boards/interfaces/services/create.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/create.board.service.interface.ts @@ -1,7 +1,7 @@ -import { BoardUserDocument } from 'src/modules/boards/entities/board.user.schema'; +import BoardUser from 'src/modules/boards/entities/board.user.schema'; import BoardDto from '../../dto/board.dto'; import BoardUserDto from '../../dto/board.user.dto'; -import { BoardDocument } from '../../entities/board.schema'; +import Board from '../../entities/board.schema'; export interface Configs { recurrent: boolean; @@ -15,9 +15,9 @@ export interface Configs { } export interface CreateBoardService { - create(boardData: BoardDto, userId: string): Promise; + create(boardData: BoardDto, userId: string): Promise; splitBoardByTeam(ownerId: string, teamId: string, configs: Configs): Promise; - saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string): Promise; + saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string): Promise; } diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index 7c6890b04..0350ff4e1 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -12,7 +12,7 @@ export interface BoardUserRepositoryInterface extends BaseInterfaceRepository; updateBoardUserRole(boardId: string, userId: string, role: string): Promise; deleteDividedBoardUsers( - dividedBoards: Board[] | ObjectId[], + dividedBoards: Board[] | ObjectId[] | string[], withSession: boolean, boardId: ObjectId | string ): Promise; diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index c5b60cdf7..995b75c09 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -53,7 +53,7 @@ export class BoardUserRepository /* DELETE BOARD USERS */ deleteDividedBoardUsers( - dividedBoards: Board[] | ObjectId[], + dividedBoards: Board[] | ObjectId[] | string[], withSession: boolean, boardId: ObjectId | string ) { diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 94ff7bf81..5fa038cc8 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -25,7 +25,10 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository ): Promise; getResponsiblesSlackId(boardId: string): Promise; getBoardByQuery(query: FilterQuery): Promise; - deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise; + deleteManySubBoards( + dividedBoards: Board[] | ObjectId[] | string[], + withSession: boolean + ): Promise; deleteBoard(boardId: string, withSession: boolean): Promise; updateBoard(boardId: string, board: Board, isNew: boolean): Promise; updateMergedSubBoard(subBoardId: string, userId: string): Promise; diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index cac910b1c..cecc1656c 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -177,7 +177,10 @@ export class BoardRepository } /* DELETE BOARD */ - deleteManySubBoards(dividedBoards: Board[] | ObjectId[], withSession: boolean): Promise { + deleteManySubBoards( + dividedBoards: Board[] | ObjectId[] | string[], + withSession: boolean + ): Promise { return this.deleteMany({ _id: { $in: dividedBoards } }, withSession); } diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index f016b053a..73ea58b2e 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -25,12 +25,13 @@ import User from 'src/modules/users/entities/user.schema'; import BoardDto from '../dto/board.dto'; import BoardUserDto from '../dto/board.user.dto'; import { Configs, CreateBoardService } from '../interfaces/services/create.board.service.interface'; -import Board, { BoardDocument } from '../entities/board.schema'; +import Board from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { UpdateTeamServiceInterface } from 'src/modules/teams/interfaces/services/update.team.service.interface'; import { addDays, addMonths, isAfter } from 'date-fns'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; import { BoardDataPopulate } from '../utils/populate-board'; +import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; export interface CreateBoardDto { maxUsers: number; @@ -44,7 +45,6 @@ export default class CreateBoardServiceImpl implements CreateBoardService { private logger = new Logger(CreateBoardServiceImpl.name); constructor( - @InjectModel(Board.name) private boardModel: Model, @InjectModel(BoardUser.name) private boardUserModel: Model, @Inject(forwardRef(() => TeamType.services.GetTeamService)) @@ -58,12 +58,15 @@ export default class CreateBoardServiceImpl implements CreateBoardService { @Inject(CommunicationsType.TYPES.services.SlackCommunicationService) private slackCommunicationService: CommunicationServiceInterface, @Inject(TYPES.repositories.BoardRepository) - private readonly boardRepository: BoardRepositoryInterface + private readonly boardRepository: BoardRepositoryInterface, + @Inject(TYPES.repositories.BoardUserRepository) + private readonly boardUserRepository: BoardUserRepositoryInterface ) {} saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) { return Promise.all( - newUsers.map((user) => this.boardUserModel.create({ ...user, board: newBoardId })) + newUsers.map((user) => this.boardUserRepository.create({ ...user, board: newBoardId })) + // newUsers.map((user) => this.boardUserModel.create({ ...user, board: newBoardId })) ); } @@ -90,7 +93,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { userId: string, isSubBoard = false, haveSubBoards = true - ): Promise { + ): Promise { const { dividedBoards = [], team } = boardData; if (haveSubBoards) { @@ -105,7 +108,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { postAnonymously: true })); - return this.boardModel.create({ + return this.boardRepository.create({ ...boardData, createdBy: userId, dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId), @@ -114,8 +117,9 @@ export default class CreateBoardServiceImpl implements CreateBoardService { }); } - return this.boardModel.create({ + return this.boardRepository.create({ ...boardData, + dividedBoards: [], createdBy: userId, isSubBoard }); @@ -146,7 +150,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { : teamUser.role; } - async create(boardData: BoardDto, userId: string, fromSchedule = false): Promise { + async create(boardData: BoardDto, userId: string, fromSchedule = false): Promise { const { team, recurrent, maxUsers, slackEnable, users, dividedBoards } = boardData; const haveDividedBoards = dividedBoards.length > 0 ? true : false; diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index a85864e28..8d77e311a 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -42,7 +42,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa private archiveChannelService: ArchiveChannelServiceInterface ) {} - async deleteSubBoards(dividedBoards: Board[] | ObjectId[], boardSession: boolean) { + async deleteSubBoards(dividedBoards: Board[] | ObjectId[] | string[], boardSession: boolean) { const deletedCount = await this.boardRepository.deleteManySubBoards( dividedBoards, boardSession @@ -52,7 +52,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa } async deleteBoardUsers( - dividedBoards: Board[] | ObjectId[], + dividedBoards: Board[] | ObjectId[] | string[], boardSession: boolean, boardId: ObjectId | string ) { @@ -78,7 +78,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa } async delete(boardId: string) { - const board = await this.boardRepository.getBoard(boardId); + const board = await this.getBoardService.getBoardById(boardId); if (!board) { throw new NotFoundException('Board not found!'); diff --git a/backend/src/modules/cards/dto/card.dto.ts b/backend/src/modules/cards/dto/card.dto.ts index b7773b820..72d76000f 100644 --- a/backend/src/modules/cards/dto/card.dto.ts +++ b/backend/src/modules/cards/dto/card.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsNotEmpty, ValidateNested } from 'class-validator'; +import { IsMongoId, IsNotEmpty, ValidateNested } from 'class-validator'; import CardItemDto from './card.item.dto'; export default class CardDto extends CardItemDto { @@ -9,4 +9,19 @@ export default class CardDto extends CardItemDto { @ValidateNested({ each: true }) @Type(() => CardItemDto) items!: CardItemDto[]; + + @ApiProperty({ description: 'Team Id' }) + @IsNotEmpty() + @IsMongoId() + createdByTeam!: string; + + @ApiProperty({ type: Date }) + @IsNotEmpty() + @IsMongoId() + createdAt!: Date; + + @ApiProperty({ description: 'User Id' }) + @IsNotEmpty() + @IsMongoId() + createdBy!: string; } diff --git a/backend/src/modules/cards/dto/card.item.dto.ts b/backend/src/modules/cards/dto/card.item.dto.ts index 8b7338582..eb967cee8 100644 --- a/backend/src/modules/cards/dto/card.item.dto.ts +++ b/backend/src/modules/cards/dto/card.item.dto.ts @@ -23,8 +23,10 @@ export default class CardItemDto { @Transform(({ value }: TransformFnParams) => value.trim()) text!: string; - @ApiPropertyOptional({ description: 'User Id' }) - createdBy?: string; + @ApiProperty({ description: 'User Id' }) + @IsNotEmpty() + @IsMongoId() + createdBy!: string; @ApiProperty({ type: CommentDto, isArray: true }) @IsNotEmpty() @@ -40,4 +42,14 @@ export default class CardItemDto { @IsNotEmpty() @IsBoolean() anonymous!: boolean; + + @ApiProperty({ description: 'Team Id' }) + @IsNotEmpty() + @IsMongoId() + createdByTeam!: string; + + @ApiProperty({ type: Date }) + @IsNotEmpty() + @IsMongoId() + createdAt!: Date; } diff --git a/backend/src/modules/columns/dto/column.dto.ts b/backend/src/modules/columns/dto/column.dto.ts index 951cc2ae6..4b40607d6 100644 --- a/backend/src/modules/columns/dto/column.dto.ts +++ b/backend/src/modules/columns/dto/column.dto.ts @@ -34,12 +34,12 @@ export default class ColumnDto { cards!: CardDto[]; @ApiProperty() - @IsOptional() + @IsNotEmpty() @IsString() - cardText?: string; + cardText!: string; @ApiProperty() - @IsOptional() + @IsNotEmpty() @IsBoolean() - isDefaultText?: boolean; + isDefaultText!: boolean; } diff --git a/backend/src/modules/columns/entities/column.schema.ts b/backend/src/modules/columns/entities/column.schema.ts index 5401fe557..d10b5aa64 100644 --- a/backend/src/modules/columns/entities/column.schema.ts +++ b/backend/src/modules/columns/entities/column.schema.ts @@ -7,7 +7,7 @@ export type ColumnDocument = Column & mongoose.Document; @Schema() export default class Column { - _id: string; + _id?: string; @Prop({ nullable: false }) title!: string; @@ -15,8 +15,8 @@ export default class Column { @Prop({ nullable: false }) color!: string; - @Prop({ nullable: false, type: [CardSchema] }) - cards!: Card[]; + @Prop({ nullable: true, type: [CardSchema] }) + cards?: Card[]; @Prop({ nullable: false, default: 'Write your comment here...' }) cardText!: string; diff --git a/backend/src/modules/comments/dto/comment.dto.ts b/backend/src/modules/comments/dto/comment.dto.ts index 86c7d18b9..6b1c14c48 100644 --- a/backend/src/modules/comments/dto/comment.dto.ts +++ b/backend/src/modules/comments/dto/comment.dto.ts @@ -1,6 +1,6 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; import { Transform, TransformFnParams } from 'class-transformer'; -import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { IsBoolean, IsMongoId, IsNotEmpty, IsString } from 'class-validator'; export default class CommentDto { @ApiProperty() @@ -9,9 +9,9 @@ export default class CommentDto { @Transform(({ value }: TransformFnParams) => value.trim()) text!: string; - @ApiPropertyOptional({ description: 'User Id' }) - @IsOptional() - createdBy?: string; + @ApiProperty({ description: 'User Id' }) + @IsMongoId() + createdBy!: string; @IsNotEmpty() @IsBoolean() From 4fdc2c0a26b848d4960cfaccb3dd56c1957e21c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Fri, 3 Mar 2023 11:18:36 +0000 Subject: [PATCH 10/22] fix: add generic to create method of baseMongo --- .../interfaces/base.repository.interface.ts | 2 +- .../mongo/mongo-generic.repository.ts | 2 +- backend/src/libs/utils/generateBoardData.ts | 2 +- backend/src/modules/boards/dto/board.dto.ts | 6 +++++- .../services/get.board.service.interface.ts | 4 +--- .../board.repository.interface.ts | 3 ++- .../boards/repositories/board.repository.ts | 6 +++++- .../boards/services/create.board.service.ts | 19 +++---------------- .../boards/services/delete.board.service.ts | 10 ++-------- .../boards/services/update.board.service.ts | 11 +++-------- backend/src/modules/cards/dto/card.dto.ts | 17 +---------------- .../src/modules/cards/dto/card.item.dto.ts | 17 +++-------------- backend/src/modules/columns/dto/column.dto.ts | 8 ++++---- .../communication/dto/createBoard.dto.ts | 10 ++++++++++ .../components/Board/DragDropArea/index.tsx | 1 + frontend/src/types/board/board.ts | 1 + 16 files changed, 44 insertions(+), 75 deletions(-) create mode 100644 backend/src/modules/communication/dto/createBoard.dto.ts diff --git a/backend/src/libs/repositories/interfaces/base.repository.interface.ts b/backend/src/libs/repositories/interfaces/base.repository.interface.ts index dc3534564..47e7389e8 100644 --- a/backend/src/libs/repositories/interfaces/base.repository.interface.ts +++ b/backend/src/libs/repositories/interfaces/base.repository.interface.ts @@ -18,7 +18,7 @@ export interface BaseInterfaceRepository { findOneByField(fields: ModelProps): Promise; - create(item: T): Promise; + create(item: Q): Promise; insertMany(listOfItems: T[]): Promise; diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 5fe42bd37..470940f4d 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -69,7 +69,7 @@ export class MongoGenericRepository implements BaseInterfaceRepository { .exec() as unknown as Promise; } - create(item: T): Promise { + create(item: Q): Promise { return this._repository.create(item); } diff --git a/backend/src/libs/utils/generateBoardData.ts b/backend/src/libs/utils/generateBoardData.ts index f010634e1..b5b723d3f 100644 --- a/backend/src/libs/utils/generateBoardData.ts +++ b/backend/src/libs/utils/generateBoardData.ts @@ -1,6 +1,6 @@ import BoardDto from 'src/modules/boards/dto/board.dto'; import BoardUserDto from 'src/modules/boards/dto/board.user.dto'; -import { CreateBoardDto } from 'src/modules/boards/services/create.board.service'; +import { CreateBoardDto } from 'src/modules/communication/dto/createBoard.dto'; export const generateSubBoardDtoData = (index: number, users: BoardUserDto[] = []): BoardDto => { return { diff --git a/backend/src/modules/boards/dto/board.dto.ts b/backend/src/modules/boards/dto/board.dto.ts index 7ff297e6f..782dfdeb5 100644 --- a/backend/src/modules/boards/dto/board.dto.ts +++ b/backend/src/modules/boards/dto/board.dto.ts @@ -30,6 +30,11 @@ export default class BoardDto { @Transform(({ value }: TransformFnParams) => value.trim()) title!: string; + @ApiPropertyOptional({ type: String }) + @IsOptional() + @IsString() + createdBy?: string; + @ApiProperty({ type: ColumnDto, isArray: true }) @ArrayNotEmpty() @ArrayMinSize(1) @@ -44,7 +49,6 @@ export default class BoardDto { isPublic!: boolean; @ApiPropertyOptional({ type: String }) - @IsNotEmpty() @IsNumber() @IsOptional() maxVotes?: number | null; diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 134cfd122..ff5e839bc 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -37,9 +37,7 @@ export interface GetBoardServiceInterface { countBoards(userId: string): Promise; - getAllBoardIdsAndTeamIdsOfUser( - userId: string - ): Promise<{ boardIds: LeanDocument[]; teamIds: any[] }>; + getAllBoardIdsAndTeamIdsOfUser(userId: string): Promise<{ boardIds: any[]; teamIds: any[] }>; getAllBoardsByTeamId(teamId: string): Promise[]>; diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 5fa038cc8..de94aede2 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -12,9 +12,10 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository getBoard(boardId: string): Promise; getBoardPopulated(boardId: string, populate?: PopulateType): Promise; getMainBoard(boardId: string): Promise; + getMainBoardOfSubBoard(boardId: string): Promise; getBoardData(boardId: string): Promise; getAllBoardsByTeamId(teamId: string): Promise; - countBoards(boardIds: string[] | ObjectId[], teamIds: string[]): Promise; + countBoards(boardIds: (string | ObjectId)[], teamIds: string[]): Promise; getCountPage(query: QueryType): Promise; getAllBoards( allBoards: boolean, diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index cecc1656c..e9be7dd9b 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -33,6 +33,10 @@ export class BoardRepository return this.findOneByFieldWithQuery({ dividedBoards: { $in: boardId } }, 'title'); } + getMainBoardOfSubBoard(boardId: string) { + return this.findOneByFieldWithQuery({ dividedBoards: { $in: boardId } }, '_id'); + } + getBoardByQuery(query: FilterQuery) { return this.findOneByFieldWithQuery(query); } @@ -111,7 +115,7 @@ export class BoardRepository return this.findOneByFieldWithQuery({ dividedBoards: { $in: [boardId] } }, 'slackChannelId'); } - countBoards(boardIds: string[] | ObjectId[], teamIds: string[]) { + countBoards(boardIds: (string | ObjectId)[], teamIds: string[]) { return this.countDocumentsWithQuery({ $and: [ { isSubBoard: false }, diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index 73ea58b2e..ded611e0a 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -1,6 +1,5 @@ import { Inject, Injectable, Logger, forwardRef } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { LeanDocument, Model } from 'mongoose'; +import { LeanDocument } from 'mongoose'; import { BoardRoles } from 'src/libs/enum/board.roles'; import { TeamRoles } from 'src/libs/enum/team.roles'; import { @@ -12,7 +11,6 @@ import { generateBoardDtoData, generateSubBoardDtoData } from 'src/libs/utils/ge import isEmpty from 'src/libs/utils/isEmpty'; import { GetBoardServiceInterface } from 'src/modules/boards/interfaces/services/get.board.service.interface'; import { TYPES } from 'src/modules/boards/interfaces/types'; -import { TeamDto } from 'src/modules/communication/dto/team.dto'; import { CommunicationServiceInterface } from 'src/modules/communication/interfaces/slack-communication.service.interface'; import * as CommunicationsType from 'src/modules/communication/interfaces/types'; import { AddCronJobDto } from 'src/modules/schedules/dto/add.cronjob.dto'; @@ -26,27 +24,17 @@ import BoardDto from '../dto/board.dto'; import BoardUserDto from '../dto/board.user.dto'; import { Configs, CreateBoardService } from '../interfaces/services/create.board.service.interface'; import Board from '../entities/board.schema'; -import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { UpdateTeamServiceInterface } from 'src/modules/teams/interfaces/services/update.team.service.interface'; import { addDays, addMonths, isAfter } from 'date-fns'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; import { BoardDataPopulate } from '../utils/populate-board'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; -export interface CreateBoardDto { - maxUsers: number; - board: BoardDto; - team: TeamDto | null; - users: BoardUserDto[]; -} - @Injectable() export default class CreateBoardServiceImpl implements CreateBoardService { private logger = new Logger(CreateBoardServiceImpl.name); constructor( - @InjectModel(BoardUser.name) - private boardUserModel: Model, @Inject(forwardRef(() => TeamType.services.GetTeamService)) private getTeamService: GetTeamServiceInterface, @Inject(forwardRef(() => TeamType.services.UpdateTeamService)) @@ -66,7 +54,6 @@ export default class CreateBoardServiceImpl implements CreateBoardService { saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) { return Promise.all( newUsers.map((user) => this.boardUserRepository.create({ ...user, board: newBoardId })) - // newUsers.map((user) => this.boardUserModel.create({ ...user, board: newBoardId })) ); } @@ -108,7 +95,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { postAnonymously: true })); - return this.boardRepository.create({ + return this.boardRepository.create({ ...boardData, createdBy: userId, dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId), @@ -117,7 +104,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { }); } - return this.boardRepository.create({ + return this.boardRepository.create({ ...boardData, dividedBoards: [], createdBy: userId, diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index 8d77e311a..c7eac91cc 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -5,15 +5,13 @@ import { NotFoundException, forwardRef } from '@nestjs/common'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model, ObjectId } from 'mongoose'; +import { ObjectId } from 'mongoose'; import { DELETE_FAILED } from 'src/libs/exceptions/messages'; import isEmpty from 'src/libs/utils/isEmpty'; import { DeleteSchedulesServiceInterface } from 'src/modules/schedules/interfaces/services/delete.schedules.service.interface'; import * as Schedules from 'src/modules/schedules/interfaces/types'; import { DeleteBoardServiceInterface } from '../interfaces/services/delete.board.service.interface'; -import Board, { BoardDocument } from '../entities/board.schema'; -import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; +import Board from '../entities/board.schema'; import * as Boards from 'src/modules/boards/interfaces/types'; import * as CommunicationTypes from 'src/modules/communication/interfaces/types'; import { GetBoardServiceInterface } from '../interfaces/services/get.board.service.interface'; @@ -26,16 +24,12 @@ import { BoardDataPopulate } from '../utils/populate-board'; @Injectable() export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterface { constructor( - @InjectModel(Board.name) - private boardModel: Model, @Inject(Boards.TYPES.repositories.BoardRepository) private readonly boardRepository: BoardRepositoryInterface, @Inject(Boards.TYPES.repositories.BoardUserRepository) private readonly boardUserRepository: BoardUserRepositoryInterface, @Inject(Schedules.TYPES.services.DeleteSchedulesService) private deleteSheduleService: DeleteSchedulesServiceInterface, - @InjectModel(BoardUser.name) - private boardUserModel: Model, @Inject(forwardRef(() => Boards.TYPES.services.GetBoardService)) private getBoardService: GetBoardServiceInterface, @Inject(CommunicationTypes.TYPES.services.SlackArchiveChannelService) diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index bc82de641..9f0f7afd9 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -22,7 +22,7 @@ import { UpdateBoardDto } from '../dto/update-board.dto'; import { ResponsibleType } from '../interfaces/responsible.interface'; import { UpdateBoardServiceInterface } from '../interfaces/services/update.board.service.interface'; import Board, { BoardDocument } from '../entities/board.schema'; -import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; +import BoardUser from '../entities/board.user.schema'; import { DELETE_FAILED, INSERT_FAILED, UPDATE_FAILED } from 'src/libs/exceptions/messages'; import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; import Column from '../../columns/entities/column.schema'; @@ -43,8 +43,6 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa private getTeamService: GetTeamServiceInterface, @Inject(CommunicationsType.TYPES.services.SlackCommunicationService) private slackCommunicationService: CommunicationServiceInterface, - @InjectModel(BoardUser.name) - private boardUserModel: Model, private socketService: SocketGateway, @Inject(Cards.TYPES.services.DeleteCardService) private deleteCardService: DeleteCardService, @@ -131,12 +129,9 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa /** * TODO: - * When the mainBoardId starts to be returned by the board, remove this query to the boardModel + * When the mainBoardId starts to be returned by the board, remove this query from the board repository */ - const mainBoardId = await this.boardModel - .findOne({ dividedBoards: { $in: boardId } }) - .select('_id') - .exec(); + const mainBoardId = await this.boardRepository.getMainBoardOfSubBoard(boardId); const promises = boardData.users .filter((boardUser) => diff --git a/backend/src/modules/cards/dto/card.dto.ts b/backend/src/modules/cards/dto/card.dto.ts index 72d76000f..b7773b820 100644 --- a/backend/src/modules/cards/dto/card.dto.ts +++ b/backend/src/modules/cards/dto/card.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsMongoId, IsNotEmpty, ValidateNested } from 'class-validator'; +import { IsNotEmpty, ValidateNested } from 'class-validator'; import CardItemDto from './card.item.dto'; export default class CardDto extends CardItemDto { @@ -9,19 +9,4 @@ export default class CardDto extends CardItemDto { @ValidateNested({ each: true }) @Type(() => CardItemDto) items!: CardItemDto[]; - - @ApiProperty({ description: 'Team Id' }) - @IsNotEmpty() - @IsMongoId() - createdByTeam!: string; - - @ApiProperty({ type: Date }) - @IsNotEmpty() - @IsMongoId() - createdAt!: Date; - - @ApiProperty({ description: 'User Id' }) - @IsNotEmpty() - @IsMongoId() - createdBy!: string; } diff --git a/backend/src/modules/cards/dto/card.item.dto.ts b/backend/src/modules/cards/dto/card.item.dto.ts index eb967cee8..d5757fae4 100644 --- a/backend/src/modules/cards/dto/card.item.dto.ts +++ b/backend/src/modules/cards/dto/card.item.dto.ts @@ -23,10 +23,9 @@ export default class CardItemDto { @Transform(({ value }: TransformFnParams) => value.trim()) text!: string; - @ApiProperty({ description: 'User Id' }) - @IsNotEmpty() - @IsMongoId() - createdBy!: string; + @ApiPropertyOptional({ description: 'User Id' }) + @IsOptional() + createdBy?: string; @ApiProperty({ type: CommentDto, isArray: true }) @IsNotEmpty() @@ -42,14 +41,4 @@ export default class CardItemDto { @IsNotEmpty() @IsBoolean() anonymous!: boolean; - - @ApiProperty({ description: 'Team Id' }) - @IsNotEmpty() - @IsMongoId() - createdByTeam!: string; - - @ApiProperty({ type: Date }) - @IsNotEmpty() - @IsMongoId() - createdAt!: Date; } diff --git a/backend/src/modules/columns/dto/column.dto.ts b/backend/src/modules/columns/dto/column.dto.ts index 4b40607d6..951cc2ae6 100644 --- a/backend/src/modules/columns/dto/column.dto.ts +++ b/backend/src/modules/columns/dto/column.dto.ts @@ -34,12 +34,12 @@ export default class ColumnDto { cards!: CardDto[]; @ApiProperty() - @IsNotEmpty() + @IsOptional() @IsString() - cardText!: string; + cardText?: string; @ApiProperty() - @IsNotEmpty() + @IsOptional() @IsBoolean() - isDefaultText!: boolean; + isDefaultText?: boolean; } diff --git a/backend/src/modules/communication/dto/createBoard.dto.ts b/backend/src/modules/communication/dto/createBoard.dto.ts new file mode 100644 index 000000000..257d06310 --- /dev/null +++ b/backend/src/modules/communication/dto/createBoard.dto.ts @@ -0,0 +1,10 @@ +import BoardDto from 'src/modules/boards/dto/board.dto'; +import BoardUserDto from 'src/modules/boards/dto/board.user.dto'; +import { TeamDto } from './team.dto'; + +export interface CreateBoardDto { + maxUsers: number; + board: BoardDto; + team: TeamDto | null; + users: BoardUserDto[]; +} diff --git a/frontend/src/components/Board/DragDropArea/index.tsx b/frontend/src/components/Board/DragDropArea/index.tsx index 9b46402c2..ce962f5e8 100644 --- a/frontend/src/components/Board/DragDropArea/index.tsx +++ b/frontend/src/components/Board/DragDropArea/index.tsx @@ -106,6 +106,7 @@ const DragDropArea: React.FC = ({ mutateBoard({ ...boardState.board, + createdBy: boardState.board.createdBy._id, team: boardState.board.team ? boardState.board.team.id : undefined, columns: columnsArray, responsible: boardState.board.users?.find((user) => user.role === BoardUserRoles.RESPONSIBLE), diff --git a/frontend/src/types/board/board.ts b/frontend/src/types/board/board.ts index 4389689a8..3342f9da0 100644 --- a/frontend/src/types/board/board.ts +++ b/frontend/src/types/board/board.ts @@ -79,6 +79,7 @@ export type UpdateBoardType = { postAnonymously: boolean; team?: string; phase?: string; + createdBy?: string; }; export type UpdateBoardPhase = { From c9120933786124dd2afb447b025dad7754bb5e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 3 Mar 2023 15:19:48 +0000 Subject: [PATCH 11/22] feat: added socket when creating board user --- .../auth/services/register.auth.service.ts | 45 +----------- .../board-user.repository.interface.ts | 12 +++- .../repositories/board-user.repository.ts | 11 +++ .../boards/services/get.board.service.ts | 70 +++++++++++++++---- 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/backend/src/modules/auth/services/register.auth.service.ts b/backend/src/modules/auth/services/register.auth.service.ts index c963577fd..de8ab530c 100644 --- a/backend/src/modules/auth/services/register.auth.service.ts +++ b/backend/src/modules/auth/services/register.auth.service.ts @@ -1,5 +1,5 @@ import { GetTokenAuthService } from 'src/modules/auth/interfaces/services/get-token.auth.service.interface'; -import { BOARD_USER_NOT_FOUND, INSERT_FAILED } from 'src/libs/exceptions/messages'; +import { INSERT_FAILED } from 'src/libs/exceptions/messages'; import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { encrypt } from 'src/libs/utils/bcrypt'; import CreateUserDto from 'src/modules/users/dto/create.user.dto'; @@ -8,21 +8,12 @@ import { TYPES } from 'src/modules/users/interfaces/types'; import * as AUTH_TYPES from 'src/modules/auth/interfaces/types'; import { RegisterAuthService } from '../interfaces/services/register.auth.service.interface'; import CreateGuestUserDto from 'src/modules/users/dto/create.guest.user.dto'; -import { InjectModel } from '@nestjs/mongoose'; -import BoardUser, { BoardUserDocument } from 'src/modules/boards/entities/board.user.schema'; -import { Model } from 'mongoose'; -import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; -import BoardGuestUserDto from 'src/modules/boards/dto/board.guest.user.dto'; -import User from 'src/modules/users/entities/user.schema'; @Injectable() export default class RegisterAuthServiceImpl implements RegisterAuthService { constructor( @Inject(TYPES.services.CreateUserService) private createUserService: CreateUserService, - @InjectModel(BoardUser.name) - private boardUserModel: Model, - private socketService: SocketGateway, @Inject(AUTH_TYPES.TYPES.services.GetTokenAuthService) private getTokenAuthService: GetTokenAuthService ) {} @@ -36,40 +27,6 @@ export default class RegisterAuthServiceImpl implements RegisterAuthService { }); } - private async getGuestBoardUser(board: string, user: string): Promise { - const userFound = await this.boardUserModel - .findOne({ board, user }) - .populate({ - path: 'user', - select: '_id firstName lastName ' - }) - .exec(); - - if (!userFound) { - throw new BadRequestException(BOARD_USER_NOT_FOUND); - } - - const { _id, firstName, lastName, isAnonymous } = userFound.user as User; - - return { - role: userFound.role, - board: String(userFound.board), - votesCount: userFound.votesCount, - user: { - _id: String(_id), - firstName, - lastName, - isAnonymous - } - }; - } - - private async sendGuestBoardUser(board: string, user: string) { - const boardUser = await this.getGuestBoardUser(board, user); - - this.socketService.sendUpdateBoardUsers(boardUser); - } - public async createGuest(guestUserData: CreateGuestUserDto) { const guestUserCreated = await this.createUserService.createGuest(guestUserData); diff --git a/backend/src/modules/boards/repositories/board-user.repository.interface.ts b/backend/src/modules/boards/repositories/board-user.repository.interface.ts index 14abe8234..4c041b27b 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.interface.ts @@ -1,14 +1,24 @@ import { ObjectId } from 'mongoose'; -import { BaseInterfaceRepository } from 'src/libs/repositories/interfaces/base.repository.interface'; +import { + BaseInterfaceRepository, + PopulateType +} from 'src/libs/repositories/interfaces/base.repository.interface'; import BoardUserDto from '../dto/board.user.dto'; import Board from '../entities/board.schema'; import BoardUser from '../entities/board.user.schema'; +import { SelectedValues } from 'src/libs/repositories/types'; export interface BoardUserRepositoryInterface extends BaseInterfaceRepository { getAllBoardsIdsOfUser(userId: string): Promise; getBoardResponsible(boardId: string): Promise; getVotesCount(boardId: string): Promise; getBoardUsers(board: string, user: string): Promise; + getBoardUser( + board: string, + user: string, + select?: SelectedValues, + populate?: PopulateType + ): Promise; createBoardUsers(boardUsers: BoardUserDto[]): Promise; updateBoardUserRole(boardId: string, userId: string, role: string): Promise; deleteDividedBoardUsers( diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index b12e054f0..b1e402daf 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -1,3 +1,4 @@ +import { PopulateType } from 'src/libs/repositories/interfaces/base.repository.interface'; import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model, ObjectId } from 'mongoose'; @@ -7,6 +8,7 @@ import BoardUserDto from '../dto/board.user.dto'; import Board from '../entities/board.schema'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; import { BoardUserRepositoryInterface } from './board-user.repository.interface'; +import { SelectedValues } from 'src/libs/repositories/types'; @Injectable() export class BoardUserRepository @@ -38,6 +40,15 @@ export class BoardUserRepository return this.findAllWithQuery({ board, user }); } + getBoardUser( + board: string, + user: string, + select?: SelectedValues, + populate?: PopulateType + ) { + return this.findOneByFieldWithQuery({ board, user }, select, populate); + } + /* CREATE BOARD USERS */ createBoardUsers(boardUsers: BoardUserDto[]) { return this.insertMany(boardUsers); diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 3e29323c6..af31a8efc 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -3,6 +3,7 @@ import TeamUser from 'src/modules/teams/entities/team.user.schema'; import Team from 'src/modules/teams/entities/teams.schema'; import { UserRepositoryInterface } from './../../users/repository/user.repository.interface'; import { + BadRequestException, ForbiddenException, Inject, Injectable, @@ -10,7 +11,12 @@ import { NotFoundException, forwardRef } from '@nestjs/common'; -import { BOARDS_NOT_FOUND, FORBIDDEN, NOT_FOUND } from 'src/libs/exceptions/messages'; +import { + BOARDS_NOT_FOUND, + BOARD_USER_NOT_FOUND, + FORBIDDEN, + NOT_FOUND +} from 'src/libs/exceptions/messages'; import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/get.team.service.interface'; import * as Teams from 'src/modules/teams/interfaces/types'; import * as Users from 'src/modules/users/interfaces/types'; @@ -30,6 +36,8 @@ import { TeamRoles } from 'src/libs/enum/team.roles'; import User from 'src/modules/users/entities/user.schema'; import { LoginGuestUserResponse } from 'src/libs/dto/response/login-guest-user.response'; import { GetTokenAuthService } from 'src/modules/auth/interfaces/services/get-token.auth.service.interface'; +import BoardGuestUserDto from '../dto/board.guest.user.dto'; +import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { @@ -46,7 +54,8 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { @Inject(TYPES.repositories.BoardUserRepository) private readonly boardUserRepository: BoardUserRepositoryInterface, @Inject(TYPES.repositories.BoardRepository) - private readonly boardRepository: BoardRepositoryInterface + private readonly boardRepository: BoardRepositoryInterface, + private socketService: SocketGateway ) {} private readonly logger = new Logger(GetBoardServiceImpl.name); @@ -179,13 +188,51 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return { accessToken, user }; } + private async getGuestBoardUser(board: string, user: string): Promise { + const userFound = await this.boardUserRepository.getBoardUser( + board, + user, + {}, + { + path: 'user', + select: '_id firstName lastName ' + } + ); + + if (!userFound) { + throw new BadRequestException(BOARD_USER_NOT_FOUND); + } + + const { _id, firstName, lastName, isAnonymous } = userFound.user as User; + + return { + role: userFound.role, + board: String(userFound.board), + votesCount: userFound.votesCount, + user: { + _id: String(_id), + firstName, + lastName, + isAnonymous + } + }; + } + + private async sendGuestBoardUser(board: string, user: string) { + const boardUser = await this.getGuestBoardUser(board, user); + + this.socketService.sendUpdateBoardUsers(boardUser); + } + private async checkIfUserCanSeeBoardAndCreatePublicBoardUsers(board: Board, user: User) { const boardUserFound = await this.getBoardUsers(board._id, user._id); let guestUser: LoginGuestUserResponse; if (!boardUserFound.length) { - if (board.isPublic) guestUser = await this.createPublicBoardUsers(board._id, user); - else { + if (board.isPublic && !user.isSAdmin) { + guestUser = (await this.createPublicBoardUsers(board._id, user)) as LoginGuestUserResponse; + this.sendGuestBoardUser(board._id, user._id); + } else { const hasPermissions = this.isAllowedToSeePrivateBoard(board, user); if (!hasPermissions) return { canSeeBoard: hasPermissions }; @@ -195,17 +242,12 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return { guestUser, canSeeBoard: true }; } - private createPublicBoardUsers(boardId: string, user: User) { - // if signed in user accesses the board but isn't a board user, create one - if (!user.isAnonymous) { - // Super Admin shouldn't automatically be added to the board as a boardUser - if (!user.isSAdmin) this.createBoardUserService.createBoardUser(boardId, user._id); - - return; - } + private async createPublicBoardUsers(boardId: string, user: User) { + const boardUserCreated = user.isAnonymous + ? await this.createBoardUserAndSendAccessToken(boardId, user._id) + : await this.createBoardUserService.createBoardUser(boardId, user._id); - // if guest user is already registered but isn't a board user, create one - return this.createBoardUserAndSendAccessToken(boardId, user._id); + if (user.isAnonymous) return boardUserCreated; } private isAllowedToSeePrivateBoard(board: Board, user: User) { From 33e64a8ccb4a8636d31a31e1c5b0ca68816859d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Fri, 3 Mar 2023 15:29:41 +0000 Subject: [PATCH 12/22] fix: remove unused imports --- .../boards/repositories/board.repository.interface.ts | 1 - .../modules/boards/repositories/board.repository.ts | 11 ----------- .../boards/services/create.board.user.service.ts | 4 +--- .../src/modules/boards/services/get.board.service.ts | 5 +---- .../src/modules/users/services/update.user.service.ts | 2 -- 5 files changed, 2 insertions(+), 21 deletions(-) diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 5f434e849..41a100c6d 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -35,6 +35,5 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository updateMergedSubBoard(subBoardId: string, userId: string): Promise; updateMergedBoard(boardId: string, newColumns: Column[]): Promise; updatedChannelId(boardId: string, channelId: string): Promise; - updateBoardPhase(boardId: string, phase: BoardPhases): Promise; updatePhase(boardId: string, phase: BoardPhases): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 0d4672d45..4214b69d7 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -169,17 +169,6 @@ export class BoardRepository return this.findOneByFieldAndUpdate({ _id: boardId }, { slackChannelId: channelId }); } - updateBoardPhase(boardId: string, phase: BoardPhases) { - return this.findOneByFieldAndUpdate( - { - _id: boardId - }, - { - phase - } - ); - } - updatePhase(boardId: string, phase: BoardPhases): Promise { return this.findOneByFieldAndUpdate( { diff --git a/backend/src/modules/boards/services/create.board.user.service.ts b/backend/src/modules/boards/services/create.board.user.service.ts index ac40a6cc6..7274aa786 100644 --- a/backend/src/modules/boards/services/create.board.user.service.ts +++ b/backend/src/modules/boards/services/create.board.user.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { BoardRoles } from 'src/libs/enum/board.roles'; import BoardUser, { BoardUserDocument } from '../entities/board.user.schema'; @@ -9,8 +9,6 @@ import BoardUserDto from '../dto/board.user.dto'; @Injectable() export default class CreateBoardUserService implements CreateBoardUserServiceInterface { - private logger = new Logger(CreateBoardUserService.name); - constructor( @InjectModel(BoardUser.name) private boardUserModel: Model diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index af31a8efc..58b129d8a 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -28,9 +28,7 @@ import { cleanBoard } from '../utils/clean-board'; import { TYPES } from '../interfaces/types'; import { BoardUserRepositoryInterface } from '../repositories/board-user.repository.interface'; import { BoardRepositoryInterface } from '../repositories/board.repository.interface'; -import Board, { BoardDocument } from '../entities/board.schema'; -import { InjectModel } from '@nestjs/mongoose'; -import { Model } from 'mongoose'; +import Board from '../entities/board.schema'; import { PopulateType } from 'src/libs/repositories/interfaces/base.repository.interface'; import { TeamRoles } from 'src/libs/enum/team.roles'; import User from 'src/modules/users/entities/user.schema'; @@ -42,7 +40,6 @@ import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; @Injectable() export default class GetBoardServiceImpl implements GetBoardServiceInterface { constructor( - @InjectModel(Board.name) private boardModel: Model, @Inject(forwardRef(() => Teams.TYPES.services.GetTeamService)) private getTeamService: GetTeamServiceInterface, @Inject(Boards.TYPES.services.CreateBoardUserService) diff --git a/backend/src/modules/users/services/update.user.service.ts b/backend/src/modules/users/services/update.user.service.ts index 186a68f11..2bfe243e6 100644 --- a/backend/src/modules/users/services/update.user.service.ts +++ b/backend/src/modules/users/services/update.user.service.ts @@ -9,14 +9,12 @@ import UpdateUserDto from '../dto/update.user.dto'; import { UpdateUserService } from '../interfaces/services/update.user.service.interface'; import { TYPES } from '../interfaces/types'; import { UserRepositoryInterface } from '../repository/user.repository.interface'; -import User, { UserDocument } from '../entities/user.schema'; import { UPDATE_FAILED } from 'src/libs/exceptions/messages'; import UserDto from '../dto/user.dto'; @Injectable() export default class updateUserServiceImpl implements UpdateUserService { constructor( - @InjectModel(User.name) private userModel: Model, @Inject(TYPES.repository) private readonly userRepository: UserRepositoryInterface, @InjectModel(ResetPassword.name) From f6500cd7848c9c127c5ee3216d81128d6d80812a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 3 Mar 2023 16:29:23 +0000 Subject: [PATCH 13/22] fix: added imports to tests --- .../auth/controller/auth.controller.spec.ts | 3 +- .../boards/services/create.board.service.ts | 227 ++++++++------- .../boards/services/delete.board.service.ts | 67 +++-- .../boards/services/get.board.service.spec.ts | 4 + .../boards/services/get.board.service.ts | 92 +++--- .../boards/services/update.board.service.ts | 270 +++++++++--------- .../controller/columns.controller.spec.ts | 3 +- .../services/update.column.service.spec.ts | 23 +- 8 files changed, 366 insertions(+), 323 deletions(-) diff --git a/backend/src/modules/auth/controller/auth.controller.spec.ts b/backend/src/modules/auth/controller/auth.controller.spec.ts index 0ee3404e2..106ff9772 100644 --- a/backend/src/modules/auth/controller/auth.controller.spec.ts +++ b/backend/src/modules/auth/controller/auth.controller.spec.ts @@ -1,4 +1,4 @@ -import { createBoardUserService } from './../../boards/boards.providers'; +import { createBoardUserService, boardUserRepository } from './../../boards/boards.providers'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -91,6 +91,7 @@ describe('AuthController', () => { SocketGateway, SchedulerRegistry, ConfigService, + boardUserRepository, { provide: CommunicationsType.TYPES.services.SlackCommunicationService, useValue: { diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index 801152960..496575572 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -55,92 +55,6 @@ export default class CreateBoardServiceImpl implements CreateBoardService { private readonly boardUserRepository: BoardUserRepositoryInterface ) {} - saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) { - return Promise.all( - newUsers.map((user) => this.boardUserRepository.create({ ...user, board: newBoardId })) - ); - } - - async createDividedBoards(boards: BoardDto[], userId: string) { - const newBoardsIds = await Promise.allSettled( - boards.map(async (board) => { - board.addCards = true; - const { users } = board; - const { _id } = await this.createBoard(board, userId, true, false); - - if (!isEmpty(users)) { - await this.createBoardUserService.saveBoardUsers(users, _id); - } - - return _id; - }) - ); - - return newBoardsIds.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : [])); - } - - async createBoard( - boardData: BoardDto, - userId: string, - isSubBoard = false, - haveSubBoards = true - ): Promise { - const { dividedBoards = [], team } = boardData; - - if (haveSubBoards) { - /** - * Add in each divided board the team id (from main board) - */ - const dividedBoardsWithTeam = dividedBoards.map((dividedBoard) => ({ - ...dividedBoard, - team, - slackEnable: boardData.slackEnable, - hideCards: true, - postAnonymously: true - })); - - return this.boardRepository.create({ - ...boardData, - createdBy: userId, - dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId), - addCards: false, - isSubBoard - }); - } - - return this.boardRepository.create({ - ...boardData, - dividedBoards: [], - createdBy: userId, - isSubBoard - }); - } - - async saveBoardUsersFromTeam(newUsers: BoardUserDto[], team: string, responsibles: string[]) { - const usersIds: string[] = []; - const teamUsers = await this.getTeamService.getUsersOfTeam(team); - - teamUsers.forEach((teamUser) => { - const user = teamUser.user as User; - - if (!usersIds.includes(user._id.toString())) { - newUsers.push({ - user: user._id.toString(), - role: responsibles.includes(user._id.toString()) - ? BoardRoles.RESPONSIBLE - : this.handleBoardUserRole(teamUser), - votesCount: 0 - }); - } - }); - } - - handleBoardUserRole(teamUser: TeamUser): string { - return teamUser.role === TeamRoles.ADMIN || teamUser.role === TeamRoles.STAKEHOLDER - ? BoardRoles.RESPONSIBLE - : teamUser.role; - } - async create(boardData: BoardDto, userId: string, fromSchedule = false): Promise { const { team, recurrent, maxUsers, slackEnable, users, dividedBoards } = boardData; @@ -201,24 +115,6 @@ export default class CreateBoardServiceImpl implements CreateBoardService { return newBoard; } - createFirstCronJob(addCronJobDto: AddCronJobDto) { - this.createSchedulesService.addCronJob({ - day: getDay(), - month: getNextMonth() - 1, - addCronJobDto - }); - } - - verifyIfIsNewJoiner = (joinedAt: Date, providerAccountCreatedAt?: Date) => { - let dateToCompare = new Date(providerAccountCreatedAt || joinedAt); - - dateToCompare = addDays(dateToCompare, 15); - - const maxDateToBeNewJoiner = addMonths(dateToCompare, 3); - - return isAfter(maxDateToBeNewJoiner, new Date()); - }; - async splitBoardByTeam( ownerId: string, teamId: string, @@ -288,7 +184,117 @@ export default class CreateBoardServiceImpl implements CreateBoardService { return board._id.toString(); } - sortUsersListByOldestCreatedDate = (users: TeamUser[]) => + saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) { + return Promise.all( + newUsers.map((user) => this.boardUserRepository.create({ ...user, board: newBoardId })) + ); + } + + /* --------------- HELPERS --------------- */ + + private async createDividedBoards(boards: BoardDto[], userId: string) { + const newBoardsIds = await Promise.allSettled( + boards.map(async (board) => { + board.addCards = true; + const { users } = board; + const { _id } = await this.createBoard(board, userId, true, false); + + if (!isEmpty(users)) { + await this.createBoardUserService.saveBoardUsers(users, _id); + } + + return _id; + }) + ); + + return newBoardsIds.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : [])); + } + + private async createBoard( + boardData: BoardDto, + userId: string, + isSubBoard = false, + haveSubBoards = true + ): Promise { + const { dividedBoards = [], team } = boardData; + + if (haveSubBoards) { + /** + * Add in each divided board the team id (from main board) + */ + const dividedBoardsWithTeam = dividedBoards.map((dividedBoard) => ({ + ...dividedBoard, + team, + slackEnable: boardData.slackEnable, + hideCards: true, + postAnonymously: true + })); + + return this.boardRepository.create({ + ...boardData, + createdBy: userId, + dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId), + addCards: false, + isSubBoard + }); + } + + return this.boardRepository.create({ + ...boardData, + dividedBoards: [], + createdBy: userId, + isSubBoard + }); + } + + private async saveBoardUsersFromTeam( + newUsers: BoardUserDto[], + team: string, + responsibles: string[] + ) { + const usersIds: string[] = []; + const teamUsers = await this.getTeamService.getUsersOfTeam(team); + + teamUsers.forEach((teamUser) => { + const user = teamUser.user as User; + + if (!usersIds.includes(user._id.toString())) { + newUsers.push({ + user: user._id.toString(), + role: responsibles.includes(user._id.toString()) + ? BoardRoles.RESPONSIBLE + : this.handleBoardUserRole(teamUser), + votesCount: 0 + }); + } + }); + } + + private handleBoardUserRole(teamUser: TeamUser): string { + return teamUser.role === TeamRoles.ADMIN || teamUser.role === TeamRoles.STAKEHOLDER + ? BoardRoles.RESPONSIBLE + : teamUser.role; + } + + private createFirstCronJob(addCronJobDto: AddCronJobDto) { + this.createSchedulesService.addCronJob({ + day: getDay(), + month: getNextMonth() - 1, + addCronJobDto + }); + } + + private verifyIfIsNewJoiner = (joinedAt: Date, providerAccountCreatedAt?: Date) => { + let dateToCompare = new Date(providerAccountCreatedAt || joinedAt); + + dateToCompare = addDays(dateToCompare, 15); + + const maxDateToBeNewJoiner = addMonths(dateToCompare, 3); + + return isAfter(maxDateToBeNewJoiner, new Date()); + }; + + private sortUsersListByOldestCreatedDate = (users: TeamUser[]) => users .map((user) => { return { @@ -298,7 +304,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { }) .sort((a, b) => Number(b.userCreated) - Number(a.userCreated)); - getAvailableUsersToBeResponsible = (availableUsers: TeamUser[]) => { + private getAvailableUsersToBeResponsible = (availableUsers: TeamUser[]) => { const availableUsersListSorted = this.sortUsersListByOldestCreatedDate(availableUsers); // returns the user who has the oldest account date @@ -317,7 +323,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { return findSelectedAvailableUserArray; }; - getRandomGroup = (usersPerTeam: number, availableUsers: TeamUser[]) => { + private getRandomGroup = (usersPerTeam: number, availableUsers: TeamUser[]) => { const randomGroupOfUsers = []; let availableUsersToBeResponsible = availableUsers.filter((user) => !user.isNewJoiner); @@ -357,9 +363,10 @@ export default class CreateBoardServiceImpl implements CreateBoardService { return randomGroupOfUsers; }; - getRandomUser = (list: TeamUser[]) => list.splice(Math.floor(Math.random() * list.length), 1)[0]; + private getRandomUser = (list: TeamUser[]) => + list.splice(Math.floor(Math.random() * list.length), 1)[0]; - handleSplitBoards = ( + private handleSplitBoards = ( maxTeams: number, teamMembers: LeanDocument[], responsibles: string[] @@ -411,7 +418,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService { return subBoards; }; - generateSubBoards( + private generateSubBoards( maxTeams: number, splitUsers: BoardUserDto[][], subBoards: BoardDto[], diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index c7eac91cc..bec4dcb79 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -36,7 +36,40 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa private archiveChannelService: ArchiveChannelServiceInterface ) {} - async deleteSubBoards(dividedBoards: Board[] | ObjectId[] | string[], boardSession: boolean) { + async delete(boardId: string) { + const board = await this.getBoardService.getBoardById(boardId); + + if (!board) { + throw new NotFoundException('Board not found!'); + } + + try { + return await this.deleteBoardBoardUsersAndSchedules(boardId, true); + } catch (error) { + throw new BadRequestException(DELETE_FAILED); + } + } + + async deleteBoardsByTeamId(teamId: string) { + const teamBoards = await this.getBoardService.getAllBoardsByTeamId(teamId); + + const promises = teamBoards.map((board) => + this.deleteBoardBoardUsersAndSchedules(board._id.toString(), false) + ); + + await Promise.all(promises).catch(() => { + throw new BadRequestException(DELETE_FAILED); + }); + + return true; + } + + /* --------------- HELPERS --------------- */ + + private async deleteSubBoards( + dividedBoards: Board[] | ObjectId[] | string[], + boardSession: boolean + ) { const deletedCount = await this.boardRepository.deleteManySubBoards( dividedBoards, boardSession @@ -45,7 +78,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa if (deletedCount !== dividedBoards.length) throw Error(DELETE_FAILED); } - async deleteBoardUsers( + private async deleteBoardUsers( dividedBoards: Board[] | ObjectId[] | string[], boardSession: boolean, boardId: ObjectId | string @@ -59,7 +92,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa if (deletedCount <= 0) throw Error(DELETE_FAILED); } - async deleteBoard(boardId: string, boardSession: boolean) { + private async deleteBoard(boardId: string, boardSession: boolean) { const result = await this.boardRepository.deleteBoard(boardId, boardSession); if (!result) throw Error(DELETE_FAILED); @@ -71,34 +104,6 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa }; } - async delete(boardId: string) { - const board = await this.getBoardService.getBoardById(boardId); - - if (!board) { - throw new NotFoundException('Board not found!'); - } - - try { - return await this.deleteBoardBoardUsersAndSchedules(boardId, true); - } catch (error) { - throw new BadRequestException(DELETE_FAILED); - } - } - - async deleteBoardsByTeamId(teamId: string) { - const teamBoards = await this.getBoardService.getAllBoardsByTeamId(teamId); - - const promises = teamBoards.map((board) => - this.deleteBoardBoardUsersAndSchedules(board._id.toString(), false) - ); - - await Promise.all(promises).catch(() => { - throw new BadRequestException(DELETE_FAILED); - }); - - return true; - } - private async deleteBoardBoardUsersAndSchedules(boardId: string, isMainBoard: boolean) { await this.boardRepository.startTransaction(); await this.boardUserRepository.startTransaction(); diff --git a/backend/src/modules/boards/services/get.board.service.spec.ts b/backend/src/modules/boards/services/get.board.service.spec.ts index 742b94251..52f996329 100644 --- a/backend/src/modules/boards/services/get.board.service.spec.ts +++ b/backend/src/modules/boards/services/get.board.service.spec.ts @@ -1,3 +1,4 @@ +import { EventEmitter2 } from '@nestjs/event-emitter'; import jwtService from 'src/libs/test-utils/mocks/jwtService.mock'; import { ConfigService } from '@nestjs/config'; import { boardUserRepository, createBoardUserService } from './../boards.providers'; @@ -19,6 +20,7 @@ import { import { updateUserService, userRepository } from 'src/modules/users/users.providers'; import { boardRepository, getBoardService } from '../boards.providers'; import { cleanBoard } from '../utils/clean-board'; +import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; // eslint-disable-next-line @typescript-eslint/no-unused-vars const fakeBoards = BoardFactory.createMany(2); @@ -41,6 +43,8 @@ describe('GetBoardServiceImpl', () => { updateTeamService, userRepository, boardUserRepository, + SocketGateway, + EventEmitter2, { provide: getModelToken('Team'), useValue: {} diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 58b129d8a..8da162dc7 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -57,18 +57,6 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { private readonly logger = new Logger(GetBoardServiceImpl.name); - async getAllBoardIdsAndTeamIdsOfUser(userId: string) { - const [boardIds, teamIds] = await Promise.all([ - this.boardUserRepository.getAllBoardsIdsOfUser(userId), - this.getTeamService.getTeamsOfUser(userId) - ]); - - return { - boardIds: boardIds.map((boardUser) => boardUser.board), - teamIds: teamIds.map((team) => team._id) - }; - } - async getUserBoardsOfLast3Months(userId: string, page: number, size?: number) { const { boardIds, teamIds } = await this.getAllBoardIdsAndTeamIdsOfUser(userId); @@ -125,21 +113,6 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return this.getBoards(false, query, page, size); } - async getBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { - const count = await this.boardRepository.getCountPage(query); - - const hasNextPage = page + 1 < Math.ceil(count / (allBoards ? count : size)); - try { - const boards = await this.boardRepository.getAllBoards(allBoards, query, page, size, count); - - return { boards: boards ?? [], hasNextPage, page }; - } catch (e) { - this.logger.error(BOARDS_NOT_FOUND); - } - - return { boards: [], hasNextPage, page }; - } - async getBoard(boardId: string, userId: string) { let board = await this.boardRepository.getBoardData(boardId); @@ -169,6 +142,53 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return { board }; } + async countBoards(userId: string) { + const { boardIds, teamIds } = await this.getAllBoardIdsAndTeamIdsOfUser(userId); + + return await this.boardRepository.countBoards(boardIds, teamIds); + } + + async getAllBoardIdsAndTeamIdsOfUser(userId: string) { + const [boardIds, teamIds] = await Promise.all([ + this.boardUserRepository.getAllBoardsIdsOfUser(userId), + this.getTeamService.getTeamsOfUser(userId) + ]); + + return { + boardIds: boardIds.map((boardUser) => boardUser.board), + teamIds: teamIds.map((team) => team._id) + }; + } + + getAllBoardsByTeamId(teamId: string) { + return this.boardRepository.getAllBoardsByTeamId(teamId); + } + + getBoardPopulated(boardId: string, populate?: PopulateType) { + return this.boardRepository.getBoardPopulated(boardId, populate); + } + + getBoardById(boardId: string) { + return this.boardRepository.getBoard(boardId); + } + + /* --------------- HELPERS --------------- */ + + private async getBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { + const count = await this.boardRepository.getCountPage(query); + + const hasNextPage = page + 1 < Math.ceil(count / (allBoards ? count : size)); + try { + const boards = await this.boardRepository.getAllBoards(allBoards, query, page, size, count); + + return { boards: boards ?? [], hasNextPage, page }; + } catch (e) { + this.logger.error(BOARDS_NOT_FOUND); + } + + return { boards: [], hasNextPage, page }; + } + private async getBoardUsers(board: string, user: string) { return await this.boardUserRepository.getBoardUsers(board, user); } @@ -257,22 +277,4 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { (user.isSAdmin || (TeamRoles.ADMIN, TeamRoles.STAKEHOLDER).includes(teamUser.role)) ); } - - async countBoards(userId: string) { - const { boardIds, teamIds } = await this.getAllBoardIdsAndTeamIdsOfUser(userId); - - return await this.boardRepository.countBoards(boardIds, teamIds); - } - - getAllBoardsByTeamId(teamId: string) { - return this.boardRepository.getAllBoardsByTeamId(teamId); - } - - getBoardPopulated(boardId: string, populate?: PopulateType) { - return this.boardRepository.getBoardPopulated(boardId, populate); - } - - getBoardById(boardId: string) { - return this.boardRepository.getBoard(boardId); - } } diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index 41db3f4f1..6dfe78fae 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -60,38 +60,6 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa private configService: ConfigService ) {} - /** - * Method to get current responsible to a specific board - * @param boardId String - * @return Board User - * @private - */ - private async getBoardResponsibleInfo(boardId: string): Promise { - const boardUser = await this.boardUserRepository.getBoardResponsible(boardId); - - if (!boardUser) { - return undefined; - } - - const user = boardUser?.user as User; - - return { id: user._id, email: user.email }; - } - - /** - * Method to get the highest value of votesCount on Board Users - * @param boardId String - * @return number - */ - private async getHighestVotesOnBoard(boardId: string): Promise { - const votesCount = await this.boardUserRepository.getVotesCount(boardId); - - return votesCount.reduce( - (prev, current) => (current.votesCount > prev ? current.votesCount : prev), - 0 - ); - } - async update(boardId: string, boardData: UpdateBoardDto) { const board = await this.boardRepository.getBoard(boardId); @@ -226,7 +194,142 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa return updatedBoard; } - async updateRegularBoard(boardId: string, boardData: UpdateBoardDto, board: Board) { + async mergeBoards(subBoardId: string, userId: string) { + const [subBoard, board] = await Promise.all([ + this.boardRepository.getBoard(subBoardId), + this.boardRepository.getBoardByQuery({ dividedBoards: { $in: [subBoardId] } }) + ]); + + if (!subBoard || !board || subBoard.submitedByUser) return null; + const team = await this.getTeamService.getTeam((board.team as ObjectId).toString()); + + if (!team) return null; + + const newSubColumns = this.generateNewSubColumns(subBoard as Board); + + const newColumns = [...(board as Board).columns]; + for (let i = 0; i < newColumns.length; i++) { + newColumns[i].cards = [...newColumns[i].cards, ...newSubColumns[i].cards]; + } + + await this.boardRepository.updateMergedSubBoard(subBoardId, userId); + + const result = await this.boardRepository.updateMergedBoard(board._id, newColumns); + + if (board.slackChannelId && board.slackEnable) { + this.slackCommunicationService.executeMergeBoardNotification({ + responsiblesChannelId: board.slackChannelId, + teamNumber: subBoard.boardNumber, + isLastSubBoard: await this.checkIfIsLastBoardToMerge(result.dividedBoards as Board[]), + boardId: subBoardId, + mainBoardId: board._id + }); + } + + return result; + } + + updateChannelId(teams: TeamDto[]) { + Promise.all( + teams.map((team) => this.boardRepository.updatedChannelId(team.boardId, team.channelId)) + ); + } + + async updateBoardParticipants(addUsers: BoardUserDto[], removeUsers: string[]) { + try { + let createdBoardUsers: BoardUser[] = []; + + if (addUsers.length > 0) createdBoardUsers = await this.addBoardUsers(addUsers); + + if (removeUsers.length > 0) await this.deleteBoardUsers(removeUsers); + + return createdBoardUsers; + } catch (error) { + throw new BadRequestException(UPDATE_FAILED); + } + } + + async updateBoardParticipantsRole(boardUserToUpdateRole: BoardUserDto) { + const user = boardUserToUpdateRole.user as unknown as User; + + const updatedBoardUsers = await this.boardUserRepository.updateBoardUserRole( + boardUserToUpdateRole.board, + user._id, + boardUserToUpdateRole.role + ); + + if (!updatedBoardUsers) { + throw new BadRequestException(UPDATE_FAILED); + } + + return updatedBoardUsers; + } + + async updatePhase(boardPhaseDto: BoardPhaseDto) { + try { + const { boardId, phase } = boardPhaseDto; + const { + slackEnable, + phase: currentPhase, + team + } = await this.boardRepository.updatePhase(boardId, phase); + + this.eventEmitter.emit(BOARD_PHASE_SERVER_UPDATED, new PhaseChangeEvent(boardPhaseDto)); + + //Sends message to SLACK + if ( + (team as Team).name === 'xgeeks' && + slackEnable === true && + currentPhase !== BoardPhases.ADDCARDS && + this.configService.getOrThrow(SLACK_ENABLE) + ) { + const message = this.generateMessage(currentPhase, boardId); + const slackMessageDto = new SlackMessageDto( + this.configService.getOrThrow(SLACK_MASTER_CHANNEL_ID), + message + ); + this.slackSendMessageService.execute(slackMessageDto); + } + } catch (err) { + throw new BadRequestException(UPDATE_FAILED); + } + } + + /* --------------- HELPERS --------------- */ + + /** + * Method to get current responsible to a specific board + * @param boardId String + * @return Board User + * @private + */ + private async getBoardResponsibleInfo(boardId: string): Promise { + const boardUser = await this.boardUserRepository.getBoardResponsible(boardId); + + if (!boardUser) { + return undefined; + } + + const user = boardUser?.user as User; + + return { id: user._id, email: user.email }; + } + + /** + * Method to get the highest value of votesCount on Board Users + * @param boardId String + * @return number + */ + private async getHighestVotesOnBoard(boardId: string): Promise { + const votesCount = await this.boardUserRepository.getVotesCount(boardId); + + return votesCount.reduce( + (prev, current) => (current.votesCount > prev ? current.votesCount : prev), + 0 + ); + } + + private async updateRegularBoard(boardId: string, boardData: UpdateBoardDto, board: Board) { /** * Validate if: * - have columns to delete @@ -290,41 +393,6 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa }); } - async mergeBoards(subBoardId: string, userId: string) { - const [subBoard, board] = await Promise.all([ - this.boardRepository.getBoard(subBoardId), - this.boardRepository.getBoardByQuery({ dividedBoards: { $in: [subBoardId] } }) - ]); - - if (!subBoard || !board || subBoard.submitedByUser) return null; - const team = await this.getTeamService.getTeam((board.team as ObjectId).toString()); - - if (!team) return null; - - const newSubColumns = this.generateNewSubColumns(subBoard as Board); - - const newColumns = [...(board as Board).columns]; - for (let i = 0; i < newColumns.length; i++) { - newColumns[i].cards = [...newColumns[i].cards, ...newSubColumns[i].cards]; - } - - await this.boardRepository.updateMergedSubBoard(subBoardId, userId); - - const result = await this.boardRepository.updateMergedBoard(board._id, newColumns); - - if (board.slackChannelId && board.slackEnable) { - this.slackCommunicationService.executeMergeBoardNotification({ - responsiblesChannelId: board.slackChannelId, - teamNumber: subBoard.boardNumber, - isLastSubBoard: await this.checkIfIsLastBoardToMerge(result.dividedBoards as Board[]), - boardId: subBoardId, - mainBoardId: board._id - }); - } - - return result; - } - private checkIfIsLastBoardToMerge(dividedBoards: Board[]): boolean { const count = dividedBoards.reduce((prev, currentValue) => { if (currentValue.submitedByUser) { @@ -384,72 +452,6 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa }); } - updateChannelId(teams: TeamDto[]) { - Promise.all( - teams.map((team) => this.boardRepository.updatedChannelId(team.boardId, team.channelId)) - ); - } - - async updateBoardParticipantsRole(boardUserToUpdateRole: BoardUserDto) { - const user = boardUserToUpdateRole.user as unknown as User; - - const updatedBoardUsers = await this.boardUserRepository.updateBoardUserRole( - boardUserToUpdateRole.board, - user._id, - boardUserToUpdateRole.role - ); - - if (!updatedBoardUsers) { - throw new BadRequestException(UPDATE_FAILED); - } - - return updatedBoardUsers; - } - - async updateBoardParticipants(addUsers: BoardUserDto[], removeUsers: string[]) { - try { - let createdBoardUsers: BoardUser[] = []; - - if (addUsers.length > 0) createdBoardUsers = await this.addBoardUsers(addUsers); - - if (removeUsers.length > 0) await this.deleteBoardUsers(removeUsers); - - return createdBoardUsers; - } catch (error) { - throw new BadRequestException(UPDATE_FAILED); - } - } - - async updatePhase(boardPhaseDto: BoardPhaseDto) { - try { - const { boardId, phase } = boardPhaseDto; - const { - slackEnable, - phase: currentPhase, - team - } = await this.boardRepository.updatePhase(boardId, phase); - - this.eventEmitter.emit(BOARD_PHASE_SERVER_UPDATED, new PhaseChangeEvent(boardPhaseDto)); - - //Sends message to SLACK - if ( - (team as Team).name === 'xgeeks' && - slackEnable === true && - currentPhase !== BoardPhases.ADDCARDS && - this.configService.getOrThrow(SLACK_ENABLE) - ) { - const message = this.generateMessage(currentPhase, boardId); - const slackMessageDto = new SlackMessageDto( - this.configService.getOrThrow(SLACK_MASTER_CHANNEL_ID), - message - ); - this.slackSendMessageService.execute(slackMessageDto); - } - } catch (err) { - throw new BadRequestException(UPDATE_FAILED); - } - } - private generateMessage(phase: string, boardId: string): string { const today = new Date(); diff --git a/backend/src/modules/columns/controller/columns.controller.spec.ts b/backend/src/modules/columns/controller/columns.controller.spec.ts index e2e4bb156..61618adf4 100644 --- a/backend/src/modules/columns/controller/columns.controller.spec.ts +++ b/backend/src/modules/columns/controller/columns.controller.spec.ts @@ -1,4 +1,4 @@ -import { createBoardUserService } from './../../boards/boards.providers'; +import { boardUserRepository, createBoardUserService } from './../../boards/boards.providers'; import { ConfigService } from '@nestjs/config'; import configService from 'src/libs/test-utils/mocks/configService.mock'; import { EventEmitterModule } from '@nestjs/event-emitter'; @@ -73,6 +73,7 @@ describe('ColumnsController', () => { createBoardUserService, getTokenAuthService, updateUserService, + boardUserRepository, { provide: getModelToken('User'), useValue: {} diff --git a/backend/src/modules/columns/services/update.column.service.spec.ts b/backend/src/modules/columns/services/update.column.service.spec.ts index 847ae7f35..e1d69a3ef 100644 --- a/backend/src/modules/columns/services/update.column.service.spec.ts +++ b/backend/src/modules/columns/services/update.column.service.spec.ts @@ -1,3 +1,8 @@ +import { ConfigService } from '@nestjs/config'; +import configService from 'src/libs/test-utils/mocks/configService.mock'; +import jwtService from 'src/libs/test-utils/mocks/jwtService.mock'; +import { getTokenAuthService } from 'src/modules/auth/auth.providers'; +import { createBoardUserService } from './../../boards/boards.providers'; import { EventEmitterModule } from '@nestjs/event-emitter'; import faker from '@faker-js/faker'; import { BadRequestException, Logger, NotFoundException } from '@nestjs/common'; @@ -22,7 +27,8 @@ import UpdateColumnServiceImpl from './update.column.service'; import DeleteCardServiceImpl from 'src/modules/cards/services/delete.card.service'; import GetBoardServiceImpl from 'src/modules/boards/services/get.board.service'; import { getTeamService, teamRepository, teamUserRepository } from 'src/modules/teams/providers'; -import { userRepository } from 'src/modules/users/users.providers'; +import { updateUserService, userRepository } from 'src/modules/users/users.providers'; +import { JwtService } from '@nestjs/jwt'; const fakeBoards = BoardFactory.createMany(2, 3, 2); @@ -53,6 +59,9 @@ describe('UpdateColumnService', () => { boardUserRepository, teamRepository, teamUserRepository, + createBoardUserService, + getTokenAuthService, + updateUserService, { provide: getModelToken(Board.name), useValue: {} @@ -72,6 +81,18 @@ describe('UpdateColumnService', () => { { provide: getModelToken('TeamUser'), useValue: {} + }, + { + provide: getModelToken('ResetPassword'), + useValue: {} + }, + { + provide: JwtService, + useValue: jwtService + }, + { + provide: ConfigService, + useValue: configService } ] }).compile(); From b0ca4b7a69159afbf5fb5ded0a52b393cb02b204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 3 Mar 2023 16:29:59 +0000 Subject: [PATCH 14/22] fix: esslint error --- backend/src/modules/auth/controller/auth.controller.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/modules/auth/controller/auth.controller.spec.ts b/backend/src/modules/auth/controller/auth.controller.spec.ts index 106ff9772..94a161cac 100644 --- a/backend/src/modules/auth/controller/auth.controller.spec.ts +++ b/backend/src/modules/auth/controller/auth.controller.spec.ts @@ -1,4 +1,4 @@ -import { createBoardUserService, boardUserRepository } from './../../boards/boards.providers'; +import { boardUserRepository, createBoardUserService } from './../../boards/boards.providers'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; From 4eb425402875dee73482d4ec6bdfd04a47c59de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Fri, 3 Mar 2023 17:20:14 +0000 Subject: [PATCH 15/22] fix: sAdmin access to board when he is a boarduser --- .../libs/guards/getBoardPermissions.guard.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/src/libs/guards/getBoardPermissions.guard.ts b/backend/src/libs/guards/getBoardPermissions.guard.ts index d0185581a..bbc6acb34 100644 --- a/backend/src/libs/guards/getBoardPermissions.guard.ts +++ b/backend/src/libs/guards/getBoardPermissions.guard.ts @@ -1,4 +1,5 @@ -import { GetBoardServiceInterface } from 'src/modules/boards/interfaces/services/get.board.service.interface'; +import { BoardUserRepository } from './../../modules/boards/repositories/board-user.repository'; +import { BoardRepository } from 'src/modules/boards/repositories/board.repository'; import { CanActivate, ExecutionContext, @@ -16,8 +17,10 @@ import { Reflector } from '@nestjs/core'; export class GetBoardGuard implements CanActivate { constructor( private readonly reflector: Reflector, - @Inject(Boards.TYPES.services.GetBoardService) - private getBoardService: GetBoardServiceInterface + @Inject(Boards.TYPES.repositories.BoardRepository) + private boardRepository: BoardRepository, + @Inject(Boards.TYPES.repositories.BoardUserRepository) + private boardUserRepository: BoardUserRepository ) {} async canActivate(context: ExecutionContext) { @@ -28,19 +31,19 @@ export class GetBoardGuard implements CanActivate { const boardId: string = request.params.boardId; try { - const { isPublic, team } = await this.getBoardService.getBoardData(boardId); - const boardUserFound = await this.getBoardService.getBoardUsers(boardId, userId); + const { isPublic, team } = await this.boardRepository.getBoardData(boardId); + const boardUserFound = await this.boardUserRepository.getBoardUsers(boardId, userId); - if (isPublic || boardUserFound.length) { + if (isPublic || boardUserFound.length || isSAdmin) { return true; } - if (!boardUserFound) { + if (!boardUserFound.length) { const { role: teamRole } = (team as Team).users.find( (teamUser: TeamUser) => (teamUser.user as User)._id.toString() === userId.toString() ); - return !isAnonymous && (isSAdmin || permissions.includes(teamRole)); + return !isAnonymous && permissions.includes(teamRole); } } catch (error) { throw new ForbiddenException(); From 89e86355f4cea0ade00757027834dbc47ccc3f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Fri, 3 Mar 2023 17:48:31 +0000 Subject: [PATCH 16/22] fix: access get board service instead of boards repositories on guard --- .../src/libs/guards/getBoardPermissions.guard.ts | 13 +++++-------- .../services/get.board.service.interface.ts | 5 +++++ .../modules/boards/services/get.board.service.ts | 14 +++++++++----- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/src/libs/guards/getBoardPermissions.guard.ts b/backend/src/libs/guards/getBoardPermissions.guard.ts index bbc6acb34..f3a4ee03d 100644 --- a/backend/src/libs/guards/getBoardPermissions.guard.ts +++ b/backend/src/libs/guards/getBoardPermissions.guard.ts @@ -1,5 +1,3 @@ -import { BoardUserRepository } from './../../modules/boards/repositories/board-user.repository'; -import { BoardRepository } from 'src/modules/boards/repositories/board.repository'; import { CanActivate, ExecutionContext, @@ -12,15 +10,14 @@ import TeamUser from 'src/modules/teams/entities/team.user.schema'; import Team from 'src/modules/teams/entities/teams.schema'; import User from 'src/modules/users/entities/user.schema'; import { Reflector } from '@nestjs/core'; +import { GetBoardServiceInterface } from 'src/modules/boards/interfaces/services/get.board.service.interface'; @Injectable() export class GetBoardGuard implements CanActivate { constructor( private readonly reflector: Reflector, - @Inject(Boards.TYPES.repositories.BoardRepository) - private boardRepository: BoardRepository, - @Inject(Boards.TYPES.repositories.BoardUserRepository) - private boardUserRepository: BoardUserRepository + @Inject(Boards.TYPES.services.GetBoardService) + private getBoardService: GetBoardServiceInterface ) {} async canActivate(context: ExecutionContext) { @@ -31,8 +28,8 @@ export class GetBoardGuard implements CanActivate { const boardId: string = request.params.boardId; try { - const { isPublic, team } = await this.boardRepository.getBoardData(boardId); - const boardUserFound = await this.boardUserRepository.getBoardUsers(boardId, userId); + const { isPublic, team } = await this.getBoardService.getBoardData(boardId); + const boardUserFound = await this.getBoardService.getBoardUsers(boardId, userId); if (isPublic || boardUserFound.length || isSAdmin) { return true; diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 743c0e030..71d264e1f 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -4,6 +4,7 @@ import { LoginGuestUserResponse } from './../../../../libs/dto/response/login-gu import Board, { BoardDocument } from '../../entities/board.schema'; import { BoardsAndPage } from '../boards-page.interface'; import UserDto from 'src/modules/users/dto/user.dto'; +import BoardUser from '../../entities/board.user.schema'; export interface GetBoardServiceInterface { getUserBoardsOfLast3Months( @@ -51,4 +52,8 @@ export interface GetBoardServiceInterface { getBoardPopulated(boardId: string, populate?: PopulateType): Promise; getBoardById(boardId: string): Promise; + + getBoardData(boardId: string): Promise; + + getBoardUsers(board: string, user: string): Promise; } diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index ac17d896e..a95e25cf1 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -155,8 +155,12 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return this.boardRepository.getBoard(boardId); } - async getBoardUsers(board: string, user: string) { - return await this.boardUserRepository.getBoardUsers(board, user); + getBoardData(boardId: string) { + return this.boardRepository.getBoardData(boardId); + } + + getBoardUsers(board: string, user: string) { + return this.boardUserRepository.getBoardUsers(board, user); } /* --------------- HELPERS --------------- */ @@ -238,11 +242,11 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { private async createPublicBoardUsers(boardId: string, user: UserDto) { if (user.isAnonymous) { const guestUser = await this.createBoardUserAndSendAccessToken(boardId, user._id); - this.sendGuestBoardUser(boardId, user._id); + await this.sendGuestBoardUser(boardId, user._id); return guestUser; } - this.createBoardUserService.createBoardUser(boardId, user._id); - this.sendGuestBoardUser(boardId, user._id); + await this.createBoardUserService.createBoardUser(boardId, user._id); + await this.sendGuestBoardUser(boardId, user._id); } } From b4634934e6e4091fa4474b6cbb440d97cdc9af5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Mon, 6 Mar 2023 13:32:34 +0000 Subject: [PATCH 17/22] fix: applied pr suggestions --- .../interfaces/base.repository.interface.ts | 16 +++++++++++++++- .../mongo/mongo-generic.repository.ts | 12 ++++++++++-- backend/src/libs/utils/generateBoardData.ts | 2 +- .../dto/createBoard.dto.ts | 2 +- .../boards/repositories/board-user.repository.ts | 12 +++++------- .../boards/repositories/board.repository.ts | 16 ++++++++-------- .../boards/services/delete.board.service.ts | 2 +- .../modules/boards/services/get.board.service.ts | 2 +- .../teams/repositories/team-user.repository.ts | 2 +- .../teams/repositories/team.repository.ts | 4 ++-- 10 files changed, 45 insertions(+), 25 deletions(-) rename backend/src/modules/{communication => boards}/dto/createBoard.dto.ts (80%) diff --git a/backend/src/libs/repositories/interfaces/base.repository.interface.ts b/backend/src/libs/repositories/interfaces/base.repository.interface.ts index 47e7389e8..12af46511 100644 --- a/backend/src/libs/repositories/interfaces/base.repository.interface.ts +++ b/backend/src/libs/repositories/interfaces/base.repository.interface.ts @@ -1,4 +1,11 @@ -import { FilterQuery, PopulateOptions, QueryOptions, SortOrder, UpdateQuery } from 'mongoose'; +import { + FilterQuery, + PopulateOptions, + ProjectionType, + QueryOptions, + SortOrder, + UpdateQuery +} from 'mongoose'; import { ModelProps, SelectedValues } from '../types'; export type PopulateType = PopulateOptions | (PopulateOptions | string)[]; @@ -12,12 +19,19 @@ export interface BaseInterfaceRepository { findAllWithQuery( query: any, + projection?: ProjectionType, selectedValues?: SelectedValues, populate?: PopulateType ): Promise; findOneByField(fields: ModelProps): Promise; + findOneByFieldWithQuery( + value: FilterQuery, + selectedValues?: SelectedValues, + populate?: PopulateType + ): Promise; + create(item: Q): Promise; insertMany(listOfItems: T[]): Promise; diff --git a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts index 470940f4d..e5b32dc69 100644 --- a/backend/src/libs/repositories/mongo/mongo-generic.repository.ts +++ b/backend/src/libs/repositories/mongo/mongo-generic.repository.ts @@ -1,4 +1,11 @@ -import { ClientSession, FilterQuery, Model, QueryOptions, UpdateQuery } from 'mongoose'; +import { + ClientSession, + FilterQuery, + Model, + ProjectionType, + QueryOptions, + UpdateQuery +} from 'mongoose'; import { BaseInterfaceRepository, PopulateType, @@ -57,12 +64,13 @@ export class MongoGenericRepository implements BaseInterfaceRepository { findAllWithQuery( query: FilterQuery, + projection?: ProjectionType, selectedValues?: SelectedValues, populate?: PopulateType, virtuals = true ): Promise { return this._repository - .find(query) + .find(query, projection) .select(selectedValues) .populate(populate) .lean({ virtuals: virtuals }) diff --git a/backend/src/libs/utils/generateBoardData.ts b/backend/src/libs/utils/generateBoardData.ts index b5b723d3f..933f92c5b 100644 --- a/backend/src/libs/utils/generateBoardData.ts +++ b/backend/src/libs/utils/generateBoardData.ts @@ -1,6 +1,6 @@ import BoardDto from 'src/modules/boards/dto/board.dto'; import BoardUserDto from 'src/modules/boards/dto/board.user.dto'; -import { CreateBoardDto } from 'src/modules/communication/dto/createBoard.dto'; +import { CreateBoardDto } from 'src/modules/boards/dto/createBoard.dto'; export const generateSubBoardDtoData = (index: number, users: BoardUserDto[] = []): BoardDto => { return { diff --git a/backend/src/modules/communication/dto/createBoard.dto.ts b/backend/src/modules/boards/dto/createBoard.dto.ts similarity index 80% rename from backend/src/modules/communication/dto/createBoard.dto.ts rename to backend/src/modules/boards/dto/createBoard.dto.ts index 257d06310..5735a4ff2 100644 --- a/backend/src/modules/communication/dto/createBoard.dto.ts +++ b/backend/src/modules/boards/dto/createBoard.dto.ts @@ -1,6 +1,6 @@ import BoardDto from 'src/modules/boards/dto/board.dto'; import BoardUserDto from 'src/modules/boards/dto/board.user.dto'; -import { TeamDto } from './team.dto'; +import { TeamDto } from '../../communication/dto/team.dto'; export interface CreateBoardDto { maxUsers: number; diff --git a/backend/src/modules/boards/repositories/board-user.repository.ts b/backend/src/modules/boards/repositories/board-user.repository.ts index b1e402daf..39ec70ccb 100644 --- a/backend/src/modules/boards/repositories/board-user.repository.ts +++ b/backend/src/modules/boards/repositories/board-user.repository.ts @@ -21,19 +21,17 @@ export class BoardUserRepository /* GET BOARD USERS */ getAllBoardsIdsOfUser(userId: string) { - return this.findAllWithQuery({ user: userId }, 'board'); + return this.findAllWithQuery({ user: userId }, null, 'board'); } getBoardResponsible(boardId: string) { - return this.findOneByFieldWithQuery( - { board: boardId, role: BoardRoles.RESPONSIBLE }, - {}, - { path: 'user' } - ); + return this.findOneByFieldWithQuery({ board: boardId, role: BoardRoles.RESPONSIBLE }, null, { + path: 'user' + }); } getVotesCount(boardId: string) { - return this.model.find({ board: boardId }, ['votesCount']).exec(); + return this.findAllWithQuery({ board: boardId }, ['votesCount']); } getBoardUsers(board: string, user: string) { diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index 4214b69d7..e25aaba4b 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -16,6 +16,9 @@ export class BoardRepository extends MongoGenericRepository implements BoardRepositoryInterface { + selectDividedBoards = + '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments'; + constructor(@InjectModel(Board.name) private model: Model) { super(model); } @@ -37,7 +40,7 @@ export class BoardRepository return this.findOneByFieldWithQuery({ dividedBoards: { $in: boardId } }, '_id'); } - getBoardByQuery(query: FilterQuery) { + getBoardByQuery(query: FilterQuery) { return this.findOneByFieldWithQuery(query); } @@ -50,11 +53,11 @@ export class BoardRepository } getAllBoardsByTeamId(teamId: string) { - return this.findAllWithQuery({ team: teamId }, 'board', undefined, false); + return this.findAllWithQuery({ team: teamId }, null, 'board', undefined, false); } getCountPage(query: QueryType) { - return this.model.find(query).countDocuments().exec(); + return this.countDocumentsWithQuery(query); } getAllBoards(allBoards: boolean, query: QueryType, page: number, size: number, count: number) { @@ -74,8 +77,7 @@ export class BoardRepository }, { path: 'dividedBoards', - select: - '-__v -createdAt -slackEnable -slackChannelId -submitedAt -id -columns.id -submitedByUser -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments', + select: this.selectDividedBoards, populate: [ { path: 'users', @@ -103,9 +105,7 @@ export class BoardRepository .sort({ updatedAt: 'desc' }) .skip(allBoards ? 0 : page * size) .limit(allBoards ? count : size) - .select( - '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments' - ) + .select(this.selectDividedBoards) .populate(boardDataToPopulate) .lean({ virtuals: true }) .exec() as unknown as Promise; diff --git a/backend/src/modules/boards/services/delete.board.service.ts b/backend/src/modules/boards/services/delete.board.service.ts index bec4dcb79..271598bdc 100644 --- a/backend/src/modules/boards/services/delete.board.service.ts +++ b/backend/src/modules/boards/services/delete.board.service.ts @@ -44,7 +44,7 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa } try { - return await this.deleteBoardBoardUsersAndSchedules(boardId, true); + return this.deleteBoardBoardUsersAndSchedules(boardId, true); } catch (error) { throw new BadRequestException(DELETE_FAILED); } diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index a95e25cf1..7982033c3 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -128,7 +128,7 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { async countBoards(userId: string) { const { boardIds, teamIds } = await this.getAllBoardIdsAndTeamIdsOfUser(userId); - return await this.boardRepository.countBoards(boardIds, teamIds); + return this.boardRepository.countBoards(boardIds, teamIds); } async getAllBoardIdsAndTeamIdsOfUser(userId: string) { diff --git a/backend/src/modules/teams/repositories/team-user.repository.ts b/backend/src/modules/teams/repositories/team-user.repository.ts index 1c37cfb1e..e16be2b2c 100644 --- a/backend/src/modules/teams/repositories/team-user.repository.ts +++ b/backend/src/modules/teams/repositories/team-user.repository.ts @@ -34,7 +34,7 @@ export class TeamUserRepository } getUsersOfTeam(teamId: string) { - return this.findAllWithQuery({ team: teamId }, 'user role isNewJoiner _id', { + return this.findAllWithQuery({ team: teamId }, null, 'user role isNewJoiner _id', { path: 'user', select: '_id firstName lastName email isSAdmin joinedAt providerAccountCreatedAt' }); diff --git a/backend/src/modules/teams/repositories/team.repository.ts b/backend/src/modules/teams/repositories/team.repository.ts index af7af3b9a..4afa113ca 100644 --- a/backend/src/modules/teams/repositories/team.repository.ts +++ b/backend/src/modules/teams/repositories/team.repository.ts @@ -30,7 +30,7 @@ export class TeamRepository } getTeamsWithUsers(teamIds: string[]): Promise { - return this.findAllWithQuery({ _id: { $in: teamIds } }, { _id: 1, name: 1 }, [ + return this.findAllWithQuery({ _id: { $in: teamIds } }, null, { _id: 1, name: 1 }, [ { path: 'users', select: 'user role isNewJoiner', @@ -47,7 +47,7 @@ export class TeamRepository } getAllTeams(): Promise { - return this.findAllWithQuery(null, null, [ + return this.findAllWithQuery(null, null, null, [ { path: 'users', select: 'user role email isNewJoiner', From 6b3600b9d5ca9508ca91ea3a381e5edae06e3348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patr=C3=ADcia=20Dias?= Date: Mon, 6 Mar 2023 14:35:51 +0000 Subject: [PATCH 18/22] fix: selectDividedBoards is defined locally --- .../src/modules/boards/repositories/board.repository.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index e25aaba4b..a255edb8c 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -16,9 +16,6 @@ export class BoardRepository extends MongoGenericRepository implements BoardRepositoryInterface { - selectDividedBoards = - '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments'; - constructor(@InjectModel(Board.name) private model: Model) { super(model); } @@ -61,6 +58,8 @@ export class BoardRepository } getAllBoards(allBoards: boolean, query: QueryType, page: number, size: number, count: number) { + const selectDividedBoards = + '-__v -createdAt -slackEnable -slackChannelId -submitedByUser -submitedAt -columns.id -columns._id -columns.cards.text -columns.cards.createdBy -columns.cards.items.text -columns.cards.items.createdBy -columns.cards.createdAt -columns.cards.items.createdAt -columns.cards._id -columns.cards.id -columns.cards.items._id -columns.cards.items.id -columns.cards.createdByTeam -columns.cards.items.createdByTeam -columns.cards.items.votes -columns.cards.items.comments -columns.cards.votes -columns.cards.comments'; const boardDataToPopulate: PopulateOptions[] = [ { path: 'createdBy', select: 'firstName lastName' }, { @@ -77,7 +76,7 @@ export class BoardRepository }, { path: 'dividedBoards', - select: this.selectDividedBoards, + select: selectDividedBoards, populate: [ { path: 'users', @@ -105,7 +104,7 @@ export class BoardRepository .sort({ updatedAt: 'desc' }) .skip(allBoards ? 0 : page * size) .limit(allBoards ? count : size) - .select(this.selectDividedBoards) + .select(selectDividedBoards) .populate(boardDataToPopulate) .lean({ virtuals: true }) .exec() as unknown as Promise; From 827a0b19601584a09f2756cb909b271e1d537c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Mon, 6 Mar 2023 16:52:50 +0000 Subject: [PATCH 19/22] fix: remove query to fetch mainBoardId from db --- backend/src/modules/boards/dto/update-board.dto.ts | 4 ++++ backend/src/modules/boards/services/update.board.service.ts | 5 +++-- .../Board/Column/partials/AlertDeleteColumn/index.tsx | 2 ++ frontend/src/components/Board/DragDropArea/index.tsx | 1 + frontend/src/components/Board/Settings/index.tsx | 2 ++ frontend/src/types/board/board.ts | 1 + 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/src/modules/boards/dto/update-board.dto.ts b/backend/src/modules/boards/dto/update-board.dto.ts index 0d742183d..2b42282b8 100644 --- a/backend/src/modules/boards/dto/update-board.dto.ts +++ b/backend/src/modules/boards/dto/update-board.dto.ts @@ -14,4 +14,8 @@ export class UpdateBoardDto extends PartialType(BoardDto) { @IsOptional() @Type(() => String) deletedColumns?: string[]; + + @ApiPropertyOptional({ type: String }) + @IsOptional() + mainBoardId?: string; } diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index 6dfe78fae..e8144e457 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -106,7 +106,8 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa * TODO: * When the mainBoardId starts to be returned by the board, remove this query from the board repository */ - const mainBoardId = await this.boardRepository.getMainBoardOfSubBoard(boardId); + const mainBoardId = boardData.mainBoardId; + //await this.boardRepository.getMainBoardOfSubBoard(boardId); const promises = boardData.users .filter((boardUser) => @@ -118,7 +119,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa const typedBoardUser = boardUser.user as unknown as User; return this.boardUserRepository.updateBoardUserRole( - mainBoardId._id, + mainBoardId, typedBoardUser._id, boardUser.role ); diff --git a/frontend/src/components/Board/Column/partials/AlertDeleteColumn/index.tsx b/frontend/src/components/Board/Column/partials/AlertDeleteColumn/index.tsx index 1edfd7595..d1e9abeff 100644 --- a/frontend/src/components/Board/Column/partials/AlertDeleteColumn/index.tsx +++ b/frontend/src/components/Board/Column/partials/AlertDeleteColumn/index.tsx @@ -45,6 +45,7 @@ const AlertDeleteColumn: React.FC = ({ columns, addCards, }, + mainBoard, } = useRecoilValue(boardInfoState); // State used to change values @@ -82,6 +83,7 @@ const AlertDeleteColumn: React.FC = ({ responsible: users?.find((user) => user.role === BoardUserRoles.RESPONSIBLE), deletedColumns, socketId, + mainBoardId: mainBoard?._id, }); }; diff --git a/frontend/src/components/Board/DragDropArea/index.tsx b/frontend/src/components/Board/DragDropArea/index.tsx index 83805c833..dbefdef96 100644 --- a/frontend/src/components/Board/DragDropArea/index.tsx +++ b/frontend/src/components/Board/DragDropArea/index.tsx @@ -124,6 +124,7 @@ const DragDropArea: React.FC = ({ columns: columnsArray, responsible: boardState.board.users?.find((user) => user.role === BoardUserRoles.RESPONSIBLE), socketId, + mainBoardId: boardState.mainBoard?._id, }); }; diff --git a/frontend/src/components/Board/Settings/index.tsx b/frontend/src/components/Board/Settings/index.tsx index 5f4417415..d0e82dc7f 100644 --- a/frontend/src/components/Board/Settings/index.tsx +++ b/frontend/src/components/Board/Settings/index.tsx @@ -71,6 +71,7 @@ const BoardSettings = ({ addCards, postAnonymously, }, + mainBoard, } = useRecoilValue(boardInfoState); const [deletedColumns, setDeletedColumns] = useRecoilState(deletedColumnsState); @@ -243,6 +244,7 @@ const BoardSettings = ({ deletedColumns, socketId, responsible: data.users?.find((user) => user.role === BoardUserRoles.RESPONSIBLE), + mainBoardId: mainBoard?._id, }); setDeletedColumns([]); diff --git a/frontend/src/types/board/board.ts b/frontend/src/types/board/board.ts index b16591002..c55ac4c47 100644 --- a/frontend/src/types/board/board.ts +++ b/frontend/src/types/board/board.ts @@ -81,6 +81,7 @@ export type UpdateBoardType = { team?: string; phase?: string; createdBy?: string; + mainBoardId?: string; }; export type UpdateBoardPhase = { From a17f7bce1c86879517c02a99133e284dfd743b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Mon, 6 Mar 2023 16:53:30 +0000 Subject: [PATCH 20/22] fix: fix boards count by team --- .../services/get.board.service.interface.ts | 2 ++ .../board.repository.interface.ts | 1 + .../boards/repositories/board.repository.ts | 10 +++++++ .../boards/services/get.board.service.ts | 4 +++ .../modules/teams/entities/teams.schema.ts | 10 ------- .../teams/services/get.team.service.ts | 26 ++++++++++++++++--- 6 files changed, 39 insertions(+), 14 deletions(-) diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 71d264e1f..0a00be528 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -56,4 +56,6 @@ export interface GetBoardServiceInterface { getBoardData(boardId: string): Promise; getBoardUsers(board: string, user: string): Promise; + + getAllMainBoards(): Promise; } diff --git a/backend/src/modules/boards/repositories/board.repository.interface.ts b/backend/src/modules/boards/repositories/board.repository.interface.ts index 41a100c6d..0d80f77bc 100644 --- a/backend/src/modules/boards/repositories/board.repository.interface.ts +++ b/backend/src/modules/boards/repositories/board.repository.interface.ts @@ -26,6 +26,7 @@ export interface BoardRepositoryInterface extends BaseInterfaceRepository ): Promise; getResponsiblesSlackId(boardId: string): Promise; getBoardByQuery(query: FilterQuery): Promise; + getAllMainBoards(): Promise; deleteManySubBoards( dividedBoards: Board[] | ObjectId[] | string[], withSession: boolean diff --git a/backend/src/modules/boards/repositories/board.repository.ts b/backend/src/modules/boards/repositories/board.repository.ts index a255edb8c..ab488a275 100644 --- a/backend/src/modules/boards/repositories/board.repository.ts +++ b/backend/src/modules/boards/repositories/board.repository.ts @@ -25,6 +25,16 @@ export class BoardRepository return this.findOneById(boardId); } + getAllMainBoards(): Promise { + return this.findAllWithQuery( + { + $and: [{ isSubBoard: false }] + }, + null, + 'team' + ); + } + getBoardPopulated(boardId: string, populate?: PopulateType) { return this.findOneById(boardId, {}, populate); } diff --git a/backend/src/modules/boards/services/get.board.service.ts b/backend/src/modules/boards/services/get.board.service.ts index 7982033c3..003566233 100644 --- a/backend/src/modules/boards/services/get.board.service.ts +++ b/backend/src/modules/boards/services/get.board.service.ts @@ -163,6 +163,10 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface { return this.boardUserRepository.getBoardUsers(board, user); } + getAllMainBoards() { + return this.boardRepository.getAllMainBoards(); + } + /* --------------- HELPERS --------------- */ private async getBoards(allBoards: boolean, query: QueryType, page = 0, size = 10) { diff --git a/backend/src/modules/teams/entities/teams.schema.ts b/backend/src/modules/teams/entities/teams.schema.ts index c68a13559..c51c33cd3 100644 --- a/backend/src/modules/teams/entities/teams.schema.ts +++ b/backend/src/modules/teams/entities/teams.schema.ts @@ -2,7 +2,6 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; import * as leanVirtualsPlugin from 'mongoose-lean-virtuals'; import BaseModel from 'src/libs/models/base.model'; -import Board from '../../boards/entities/board.schema'; import TeamUser from './team.user.schema'; export type TeamDocument = Team & Document; @@ -16,8 +15,6 @@ export default class Team extends BaseModel { @Prop({ nullable: false, required: true, unique: true }) name!: string; - boards?: Board[]; - users?: TeamUser[]; } @@ -31,10 +28,3 @@ TeamSchema.virtual('users', { foreignField: 'team', justOne: false }); - -TeamSchema.virtual('boards', { - ref: 'Board', - localField: '_id', - foreignField: 'team', - justOne: false -}); diff --git a/backend/src/modules/teams/services/get.team.service.ts b/backend/src/modules/teams/services/get.team.service.ts index e0469fe51..aac832721 100644 --- a/backend/src/modules/teams/services/get.team.service.ts +++ b/backend/src/modules/teams/services/get.team.service.ts @@ -2,10 +2,12 @@ import { ForbiddenException, Inject, Injectable } from '@nestjs/common'; import { GetTeamServiceInterface } from '../interfaces/services/get.team.service.interface'; import Team from '../entities/teams.schema'; import { TYPES } from '../interfaces/types'; +import * as Boards from 'src/modules/boards/interfaces/types'; import { TeamRepositoryInterface } from '../repositories/team.repository.interface'; import { TeamUserRepositoryInterface } from '../repositories/team-user.repository.interface'; import User from 'src/modules/users/entities/user.schema'; import UserDto from 'src/modules/users/dto/user.dto'; +import { GetBoardServiceInterface } from 'src/modules/boards/interfaces/services/get.board.service.interface'; @Injectable() export default class GetTeamService implements GetTeamServiceInterface { @@ -13,7 +15,9 @@ export default class GetTeamService implements GetTeamServiceInterface { @Inject(TYPES.repositories.TeamRepository) private readonly teamRepository: TeamRepositoryInterface, @Inject(TYPES.repositories.TeamUserRepository) - private readonly teamUserRepository: TeamUserRepositoryInterface + private readonly teamUserRepository: TeamUserRepositoryInterface, + @Inject(Boards.TYPES.services.GetBoardService) + private getBoardService: GetBoardServiceInterface ) {} countTeams(userId: string) { @@ -47,9 +51,17 @@ export default class GetTeamService implements GetTeamServiceInterface { teamsUser.map((teamUser) => teamUser._id) ); - return teams.map((team) => { - return { ...team, boardsCount: team.boards?.length ?? 0, boards: undefined }; + const allBoards = await this.getBoardService.getAllMainBoards(); + + const teamsResult = teams.map((team) => { + return { + ...team, + boardsCount: + allBoards.filter((board) => String(board.team) === String(team._id)).length ?? 0 + }; }); + + return teamsResult; } getUsersOnlyWithTeams(users: User[]) { @@ -65,8 +77,14 @@ export default class GetTeamService implements GetTeamServiceInterface { const teams = await this.teamRepository.getAllTeams(); + const allBoards = await this.getBoardService.getAllMainBoards(); + return teams.map((team) => { - return { ...team, boardsCount: team.boards?.length ?? 0, boards: undefined }; + return { + ...team, + boardsCount: + allBoards.filter((board) => String(board.team) === String(team._id)).length ?? 0 + }; }); } From b9644d2d8dd6a191ecceb90eb73fbe6a7c9d8b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Mon, 6 Mar 2023 18:17:47 +0000 Subject: [PATCH 21/22] fix: fix missed imports on testing files --- .../auth/controller/auth.controller.spec.ts | 16 +++++---- .../validate-user.auth.service.spec.ts | 35 ++++++++++++++++++- .../controller/boards.controller.spec.ts | 3 +- .../controller/columns.controller.spec.ts | 1 + 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/backend/src/modules/auth/controller/auth.controller.spec.ts b/backend/src/modules/auth/controller/auth.controller.spec.ts index 94a161cac..d89ee3e24 100644 --- a/backend/src/modules/auth/controller/auth.controller.spec.ts +++ b/backend/src/modules/auth/controller/auth.controller.spec.ts @@ -1,4 +1,11 @@ -import { boardUserRepository, createBoardUserService } from './../../boards/boards.providers'; +import { + boardRepository, + boardUserRepository, + createBoardService, + createBoardUserService, + getBoardApplication, + getBoardService +} from './../../boards/boards.providers'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -18,12 +25,6 @@ import { registerAuthService } from 'src/modules/auth/auth.providers'; import AuthController from 'src/modules/auth/controller/auth.controller'; -import { - boardRepository, - createBoardService, - getBoardApplication, - getBoardService -} from 'src/modules/boards/boards.providers'; import EmailModule from 'src/modules/mailer/mailer.module'; import { createTeamService, @@ -92,6 +93,7 @@ describe('AuthController', () => { SchedulerRegistry, ConfigService, boardUserRepository, + createBoardService, { provide: CommunicationsType.TYPES.services.SlackCommunicationService, useValue: { diff --git a/backend/src/modules/auth/services/validate-user.auth.service.spec.ts b/backend/src/modules/auth/services/validate-user.auth.service.spec.ts index 3d6ef4c1c..e0292ea2a 100644 --- a/backend/src/modules/auth/services/validate-user.auth.service.spec.ts +++ b/backend/src/modules/auth/services/validate-user.auth.service.spec.ts @@ -1,4 +1,5 @@ import { ConfigService } from '@nestjs/config'; +import { EventEmitterModule } from '@nestjs/event-emitter'; import { JwtService } from '@nestjs/jwt'; import { getModelToken } from '@nestjs/mongoose'; import { Test } from '@nestjs/testing'; @@ -7,6 +8,13 @@ import configService from 'src/libs/test-utils/mocks/configService.mock'; import jwtService from 'src/libs/test-utils/mocks/jwtService.mock'; import mockedUser from 'src/libs/test-utils/mocks/user.mock'; import ValidateUserAuthServiceImpl from 'src/modules/auth/services/validate-user.auth.service'; +import { + boardRepository, + boardUserRepository, + createBoardUserService, + getBoardService +} from 'src/modules/boards/boards.providers'; +import SocketGateway from 'src/modules/socket/gateway/socket.gateway'; import { getTeamService, teamRepository, @@ -15,7 +23,12 @@ import { } from 'src/modules/teams/providers'; import { GetUserService } from 'src/modules/users/interfaces/services/get.user.service.interface'; import { TYPES } from 'src/modules/users/interfaces/types'; -import { getUserService, userRepository } from 'src/modules/users/users.providers'; +import { + getUserService, + updateUserService, + userRepository +} from 'src/modules/users/users.providers'; +import { getTokenAuthService } from '../auth.providers'; jest.mock('bcrypt'); jest.mock('src/modules/schedules/services/create.schedules.service.ts'); @@ -38,14 +51,22 @@ describe('The AuthenticationService', () => { (bcrypt.compare as jest.Mock) = bcryptCompare; const module = await Test.createTestingModule({ + imports: [EventEmitterModule.forRoot()], providers: [ ValidateUserAuthServiceImpl, + SocketGateway, getUserService, getTeamService, userRepository, teamRepository, teamUserRepository, updateTeamService, + getBoardService, + createBoardUserService, + getTokenAuthService, + boardUserRepository, + boardRepository, + updateUserService, { provide: ConfigService, useValue: configService @@ -54,6 +75,14 @@ describe('The AuthenticationService', () => { provide: JwtService, useValue: jwtService }, + { + provide: getModelToken('Board'), + useValue: {} + }, + { + provide: getModelToken('BoardUser'), + useValue: {} + }, { provide: getModelToken('User'), useValue: usersRepository @@ -65,6 +94,10 @@ describe('The AuthenticationService', () => { { provide: getModelToken('TeamUser'), useValue: {} + }, + { + provide: getModelToken('ResetPassword'), + useValue: {} } ] }).compile(); diff --git a/backend/src/modules/boards/controller/boards.controller.spec.ts b/backend/src/modules/boards/controller/boards.controller.spec.ts index dea68ac30..5606963bf 100644 --- a/backend/src/modules/boards/controller/boards.controller.spec.ts +++ b/backend/src/modules/boards/controller/boards.controller.spec.ts @@ -1,4 +1,3 @@ -import { createBoardUserService } from './../boards.providers'; import { ConfigService } from '@nestjs/config'; import configService from 'src/libs/test-utils/mocks/configService.mock'; import { EventEmitterModule } from '@nestjs/event-emitter'; @@ -10,6 +9,7 @@ import { boardUserRepository, createBoardApplication, createBoardService, + createBoardUserService, deleteBoardApplication, deleteBoardService, getBoardApplication, @@ -75,6 +75,7 @@ describe('BoardsController', () => { createBoardUserService, getTokenAuthService, updateUserService, + getBoardService, { provide: getModelToken('User'), useValue: {} diff --git a/backend/src/modules/columns/controller/columns.controller.spec.ts b/backend/src/modules/columns/controller/columns.controller.spec.ts index 61618adf4..2244c2c5a 100644 --- a/backend/src/modules/columns/controller/columns.controller.spec.ts +++ b/backend/src/modules/columns/controller/columns.controller.spec.ts @@ -74,6 +74,7 @@ describe('ColumnsController', () => { getTokenAuthService, updateUserService, boardUserRepository, + createBoardUserService, { provide: getModelToken('User'), useValue: {} From d037056d9c664364c1d904be1e0bf55338695ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1tia=20Antunes?= Date: Tue, 7 Mar 2023 09:20:15 +0000 Subject: [PATCH 22/22] fix: remove comments from code --- backend/src/modules/boards/services/update.board.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/modules/boards/services/update.board.service.ts b/backend/src/modules/boards/services/update.board.service.ts index c5de353f3..7fff4e756 100644 --- a/backend/src/modules/boards/services/update.board.service.ts +++ b/backend/src/modules/boards/services/update.board.service.ts @@ -103,12 +103,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa await Promise.all(promises); } - /** - * TODO: - * When the mainBoardId starts to be returned by the board, remove this query from the board repository - */ const mainBoardId = boardData.mainBoardId; - //await this.boardRepository.getMainBoardOfSubBoard(boardId); const promises = boardData.users .filter((boardUser) =>