From 026a8b26a0ece6228b4ff1c1ec811d08a491a430 Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Fri, 12 Jul 2024 08:20:55 -0400 Subject: [PATCH] feat: created endpoint for folder and files existence --- src/modules/file/file.repository.spec.ts | 22 ++++ src/modules/file/file.repository.ts | 27 ++++- src/modules/file/file.usecase.ts | 14 +++ .../dto/files-existence-in-folder.dto.spec.ts | 76 ++++++++++++ .../dto/files-existence-in-folder.dto.ts | 32 ++++++ .../folder-existence-in-folder.dto.spec.ts | 56 +++++++++ .../dto/folder-existence-in-folder.dto.ts | 22 ++++ src/modules/folder/folder.controller.spec.ts | 108 ++++++++++++++++++ src/modules/folder/folder.controller.ts | 53 +++++++++ src/modules/folder/folder.repository.spec.ts | 40 +++++++ src/modules/folder/folder.repository.ts | 35 +++++- src/modules/folder/folder.usecase.spec.ts | 71 ++++++++++++ src/modules/folder/folder.usecase.ts | 23 ++++ 13 files changed, 577 insertions(+), 2 deletions(-) create mode 100644 src/modules/folder/dto/files-existence-in-folder.dto.spec.ts create mode 100644 src/modules/folder/dto/files-existence-in-folder.dto.ts create mode 100644 src/modules/folder/dto/folder-existence-in-folder.dto.spec.ts create mode 100644 src/modules/folder/dto/folder-existence-in-folder.dto.ts diff --git a/src/modules/file/file.repository.spec.ts b/src/modules/file/file.repository.spec.ts index d65706ce0..82de6915c 100644 --- a/src/modules/file/file.repository.spec.ts +++ b/src/modules/file/file.repository.spec.ts @@ -6,6 +6,7 @@ import { FileStatus } from './file.domain'; import { FileModel } from './file.model'; import { FileRepository, SequelizeFileRepository } from './file.repository'; import { Op } from 'sequelize'; +import { v4 } from 'uuid'; describe('FileRepository', () => { let repository: FileRepository; @@ -93,4 +94,25 @@ describe('FileRepository', () => { expect(result).toEqual(fileSizes); }); }); + + describe('findFileByFolderUuid', () => { + const folderUuid = v4(); + + it('When a file is searched, then it should handle the dynamic input', async () => { + const searchCriteria = { plainName: ['Report'], type: 'pdf' }; + + await repository.findFileByFolderUuid(folderUuid, searchCriteria); + + expect(fileModel.findAll).toHaveBeenCalledWith({ + where: expect.objectContaining({ + folderUuid, + plainName: { + [Op.in]: searchCriteria.plainName, + }, + type: searchCriteria.type, + status: FileStatus.EXISTS, + }), + }); + }); + }); }); diff --git a/src/modules/file/file.repository.ts b/src/modules/file/file.repository.ts index 77a3da2b4..e81811e5e 100644 --- a/src/modules/file/file.repository.ts +++ b/src/modules/file/file.repository.ts @@ -1,7 +1,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/sequelize'; import { File, FileAttributes, FileOptions, FileStatus } from './file.domain'; -import { FindOptions, Op, Sequelize } from 'sequelize'; +import { FindOptions, Op, Sequelize, WhereOptions } from 'sequelize'; import { Literal } from 'sequelize/types/utils'; import { User } from '../user/user.domain'; @@ -43,6 +43,10 @@ export interface FileRepository { where: Partial>, nameFilter: Pick, ): Promise; + findFileByFolderUuid( + folderUuid: Folder['uuid'], + searchBy: { plainName: File['plainName'][]; type?: File['type'] }, + ): Promise; findByNameAndFolderUuid( name: FileAttributes['name'], type: FileAttributes['type'], @@ -563,6 +567,27 @@ export class SequelizeFileRepository implements FileRepository { return file ? this.toDomain(file) : null; } + async findFileByFolderUuid( + folderUuid: Folder['uuid'], + searchBy: { plainName: File['plainName'][]; type?: File['type'] }, + ): Promise { + const where: WhereOptions = { + folderUuid, + ...(searchBy?.type ? { type: searchBy.type } : null), + status: FileStatus.EXISTS, + }; + + if (searchBy?.plainName?.length) { + where.plainName = { [Op.in]: searchBy.plainName }; + } + + const files = await this.fileModel.findAll({ + where, + }); + + return files.map(this.toDomain.bind(this)); + } + async updateByFieldIdAndUserId( fileId: FileAttributes['fileId'], userId: FileAttributes['userId'], diff --git a/src/modules/file/file.usecase.ts b/src/modules/file/file.usecase.ts index 0b6264d37..7d8c069ba 100644 --- a/src/modules/file/file.usecase.ts +++ b/src/modules/file/file.usecase.ts @@ -32,6 +32,7 @@ import { v4 } from 'uuid'; import { CreateFileDto } from './dto/create-file.dto'; import { UpdateFileMetaDto } from './dto/update-file-meta.dto'; import { WorkspaceAttributes } from '../workspaces/attributes/workspace.attributes'; +import { Folder } from '../folder/folder.domain'; type SortParams = Array<[SortableFileAttributes, 'ASC' | 'DESC']>; @@ -138,6 +139,19 @@ export class FileUseCases { return newFile; } + async searchFilesInFolder( + folderUuid: Folder['uuid'], + { + plainNames, + type, + }: { plainNames: File['plainName'][]; type?: File['type'] }, + ): Promise { + return this.fileRepository.findFileByFolderUuid(folderUuid, { + plainName: plainNames, + type, + }); + } + async updateFileMetaData( user: User, fileUuid: File['uuid'], diff --git a/src/modules/folder/dto/files-existence-in-folder.dto.spec.ts b/src/modules/folder/dto/files-existence-in-folder.dto.spec.ts new file mode 100644 index 000000000..38f5b87f2 --- /dev/null +++ b/src/modules/folder/dto/files-existence-in-folder.dto.spec.ts @@ -0,0 +1,76 @@ +import { validate } from 'class-validator'; +import { plainToInstance } from 'class-transformer'; +import { CheckFileExistenceInFolderDto } from './files-existence-in-folder.dto'; + +describe('CheckFileExistenceInFolderDto', () => { + it('When valid data is passed, then no errors should be returned', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName: ['file1', 'file2'], + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + }); + + it('When a single string is passed for plainName, then it should be transformed into an array and validate successfully', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName: 'file1', + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + expect(dto.plainName).toEqual(['file1']); + }); + + it('When plainName array exceeds max size, then it should fail', async () => { + const plainName = Array.from({ length: 51 }, (_, i) => `file${i + 1}`); + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName, + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + expect(errors[0].constraints).toBeDefined(); + }); + + it('When plainName contains non-string values, then it should fail', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName: [1, 2, 3], + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + }); + + it('When plainName is not provided, then it should fail', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + }); + + it('When plainName is an empty array, then it should validate successfully', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName: [], + type: 'txt', + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + }); + + it('When type is not provided, then it should validate successfully', async () => { + const dto = plainToInstance(CheckFileExistenceInFolderDto, { + plainName: ['file1', 'file2'], + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + }); +}); diff --git a/src/modules/folder/dto/files-existence-in-folder.dto.ts b/src/modules/folder/dto/files-existence-in-folder.dto.ts new file mode 100644 index 000000000..0bfaccff8 --- /dev/null +++ b/src/modules/folder/dto/files-existence-in-folder.dto.ts @@ -0,0 +1,32 @@ +import { Transform } from 'class-transformer'; +import { IsString, ArrayMaxSize, IsArray, IsOptional } from 'class-validator'; +import { FileAttributes } from '../../file/file.domain'; +import { ApiProperty } from '@nestjs/swagger'; + +export class CheckFileExistenceInFolderDto { + @ApiProperty({ + description: 'Type of file', + example: 'pdf', + required: false, + }) + @IsString() + @IsOptional() + type?: FileAttributes['type']; + + @ApiProperty({ + description: 'Plain name of file', + example: 'example', + }) + @IsArray() + @ArrayMaxSize(50, { + message: 'Names parameter cannot contain more than 50 names', + }) + @IsString({ each: true }) + @Transform(({ value }) => { + if (typeof value === 'string') { + return [value]; + } + return value; + }) + plainName: FileAttributes['plainName'][]; +} diff --git a/src/modules/folder/dto/folder-existence-in-folder.dto.spec.ts b/src/modules/folder/dto/folder-existence-in-folder.dto.spec.ts new file mode 100644 index 000000000..e0352dfe0 --- /dev/null +++ b/src/modules/folder/dto/folder-existence-in-folder.dto.spec.ts @@ -0,0 +1,56 @@ +import { validate } from 'class-validator'; +import { plainToInstance } from 'class-transformer'; +import { CheckFoldersExistenceDto } from './folder-existence-in-folder.dto'; + +describe('CheckFoldersExistenceDto', () => { + it('When valid data is passed, then no errors should be returned', async () => { + const dto = plainToInstance(CheckFoldersExistenceDto, { + plainName: ['folder1', 'folder2'], + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + }); + + it('When a single string is passed, then it should be transformed into an array and validate successfully', async () => { + const dto = plainToInstance(CheckFoldersExistenceDto, { + plainName: 'folder1', + }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + expect(dto.plainName).toEqual(['folder1']); + }); + + it('When plainName array exceeds max size, then it should fail', async () => { + const plainName = Array.from({ length: 51 }, (_, i) => `folder${i + 1}`); + const dto = plainToInstance(CheckFoldersExistenceDto, { plainName }); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + expect(errors[0].constraints).toBeDefined(); + }); + + it('When plainName contains non-string values, then it should fail', async () => { + const dto = plainToInstance(CheckFoldersExistenceDto, { + plainName: [1, 2, 3], + }); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + }); + + it('When plainName is not provided, then it should fail', async () => { + const dto = plainToInstance(CheckFoldersExistenceDto, {}); + + const errors = await validate(dto); + expect(errors.length).toBeGreaterThan(0); + }); + + it('When plainName is an empty array, then it should validate successfully', async () => { + const dto = plainToInstance(CheckFoldersExistenceDto, { plainName: [] }); + + const errors = await validate(dto); + expect(errors.length).toBe(0); + }); +}); diff --git a/src/modules/folder/dto/folder-existence-in-folder.dto.ts b/src/modules/folder/dto/folder-existence-in-folder.dto.ts new file mode 100644 index 000000000..71c47b769 --- /dev/null +++ b/src/modules/folder/dto/folder-existence-in-folder.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsString, ArrayMaxSize, IsArray } from 'class-validator'; + +export class CheckFoldersExistenceDto { + @ApiProperty({ + description: 'Plain name of folder', + example: 'my folder', + }) + @IsArray() + @ArrayMaxSize(50, { + message: 'Names parameter cannot contain more than 50 names', + }) + @IsString({ each: true }) + @Transform(({ value }) => { + if (typeof value === 'string') { + return [value]; + } + return value; + }) + plainName: string[]; +} diff --git a/src/modules/folder/folder.controller.spec.ts b/src/modules/folder/folder.controller.spec.ts index e662a718b..7cee20c33 100644 --- a/src/modules/folder/folder.controller.spec.ts +++ b/src/modules/folder/folder.controller.spec.ts @@ -14,6 +14,7 @@ import { FolderUseCases } from './folder.usecase'; import { CalculateFolderSizeTimeoutException } from './exception/calculate-folder-size-timeout.exception'; import { User } from '../user/user.domain'; import { FileStatus } from '../file/file.domain'; +import { InvalidParentFolderException } from './exception/invalid-parent-folder'; describe('FolderController', () => { let folderController: FolderController; @@ -351,4 +352,111 @@ describe('FolderController', () => { }); }); }); + + describe('checkFoldersExistenceInFolder', () => { + const user = newUser(); + const folderUuid = v4(); + const plainNames = ['Documents', 'Photos']; + + it('When valid folderUuid and plainNames are provided, then it should return existent folders', async () => { + const mockFolders = [ + newFolder({ attributes: { plainName: 'Documents', userId: user.id } }), + newFolder({ attributes: { plainName: 'Photos', userId: user.id } }), + ]; + + jest + .spyOn(folderUseCases, 'searchFoldersInFolder') + .mockResolvedValue(mockFolders); + + const result = await folderController.checkFoldersExistenceInFolder( + user, + folderUuid, + { plainName: plainNames }, + ); + + expect(result).toEqual({ existentFolders: mockFolders }); + expect(folderUseCases.searchFoldersInFolder).toHaveBeenCalledWith( + user, + folderUuid, + { plainNames }, + ); + }); + + it('When folders are not found, then it should return an empty array', async () => { + jest.spyOn(folderUseCases, 'searchFoldersInFolder').mockResolvedValue([]); + + const result = await folderController.checkFoldersExistenceInFolder( + user, + folderUuid, + { plainName: plainNames }, + ); + + expect(result).toEqual({ existentFolders: [] }); + expect(folderUseCases.searchFoldersInFolder).toHaveBeenCalledWith( + user, + folderUuid, + { plainNames }, + ); + }); + }); + + describe('checkFilesExistenceInFolder', () => { + const user = newUser(); + const folderUuid = v4(); + const plainName = ['Report.pdf', 'Image.png']; + const type = 'document'; + + it('When files exist matching the criteria, then it should return the files', async () => { + const parentFolder = newFolder({ attributes: { uuid: folderUuid } }); + const mockFiles = [ + newFile({ attributes: { plainName: 'Report.pdf', type: 'document' } }), + newFile({ attributes: { plainName: 'Image.png', type: 'image' } }), + ]; + + jest + .spyOn(folderUseCases, 'getFolderByUuidAndUser') + .mockResolvedValue(parentFolder); + jest + .spyOn(fileUseCases, 'searchFilesInFolder') + .mockResolvedValue(mockFiles); + + const result = await folderController.checkFilesExistenceInFolder( + user, + folderUuid, + { plainName, type }, + ); + + expect(result).toEqual({ existentFiles: mockFiles }); + }); + + it('When no files match the criteria, then it should return an empty array', async () => { + const parentFolder = newFolder({ attributes: { uuid: folderUuid } }); + + jest + .spyOn(folderUseCases, 'getFolderByUuidAndUser') + .mockResolvedValue(parentFolder); + jest.spyOn(fileUseCases, 'searchFilesInFolder').mockResolvedValue([]); + + const result = await folderController.checkFilesExistenceInFolder( + user, + folderUuid, + { plainName, type }, + ); + + expect(result).toEqual({ existentFiles: [] }); + }); + + it('When the parent folder does not exist, then it should throw', async () => { + jest + .spyOn(folderUseCases, 'getFolderByUuidAndUser') + .mockResolvedValue(null); + + await expect( + folderController.checkFilesExistenceInFolder(user, folderUuid, { + plainName, + type, + }), + ).rejects.toThrow(InvalidParentFolderException); + }); + }); }); diff --git a/src/modules/folder/folder.controller.ts b/src/modules/folder/folder.controller.ts index d4261860c..a39d9f837 100644 --- a/src/modules/folder/folder.controller.ts +++ b/src/modules/folder/folder.controller.ts @@ -46,6 +46,9 @@ import { ValidateUUIDPipe } from '../workspaces/pipes/validate-uuid.pipe'; import { UpdateFolderMetaDto } from './dto/update-folder-meta.dto'; import { WorkspacesInBehalfValidationFolder } from '../workspaces/guards/workspaces-resources-in-behalf.decorator'; import { CreateFolderDto } from './dto/create-folder.dto'; +import { CheckFoldersExistenceDto } from './dto/folder-existence-in-folder.dto'; +import { InvalidParentFolderException } from './exception/invalid-parent-folder'; +import { CheckFileExistenceInFolderDto } from './dto/files-existence-in-folder.dto'; const foldersStatuses = ['ALL', 'EXISTS', 'TRASHED', 'DELETED'] as const; @@ -335,6 +338,56 @@ export class FolderController { }; } + @Get('/content/:uuid/folders/existence') + @WorkspacesInBehalfValidationFolder([ + { sourceKey: 'params', fieldName: 'uuid', newFieldName: 'itemId' }, + ]) + async checkFoldersExistenceInFolder( + @UserDecorator() user: User, + @Param('uuid') folderUuid: string, + @Query() query: CheckFoldersExistenceDto, + ) { + const { plainName } = query; + + const folders = await this.folderUseCases.searchFoldersInFolder( + user, + folderUuid, + { + plainNames: plainName, + }, + ); + + return { existentFolders: folders }; + } + + @Get('/content/:uuid/files/existence') + @WorkspacesInBehalfValidationFolder([ + { sourceKey: 'params', fieldName: 'uuid', newFieldName: 'itemId' }, + ]) + async checkFilesExistenceInFolder( + @UserDecorator() user: User, + @Param('uuid') folderUuid: string, + @Query() query: CheckFileExistenceInFolderDto, + ) { + const { plainName, type } = query; + + const parentFolder = await this.folderUseCases.getFolderByUuidAndUser( + folderUuid, + user, + ); + + if (!parentFolder) { + throw new InvalidParentFolderException('Parent folder not valid!'); + } + + const files = await this.fileUseCases.searchFilesInFolder( + parentFolder.uuid, + { plainNames: plainName, type }, + ); + + return { existentFiles: files }; + } + @Get('/content/:uuid') @ApiOperation({ summary: 'Gets folder content', diff --git a/src/modules/folder/folder.repository.spec.ts b/src/modules/folder/folder.repository.spec.ts index d287bdaf1..14814996b 100644 --- a/src/modules/folder/folder.repository.spec.ts +++ b/src/modules/folder/folder.repository.spec.ts @@ -5,6 +5,8 @@ import { FolderModel } from './folder.model'; import { Folder } from './folder.domain'; import { newFolder } from '../../../test/fixtures'; import { FileStatus } from '../file/file.domain'; +import { v4 } from 'uuid'; +import { Op } from 'sequelize'; jest.mock('./folder.model', () => ({ FolderModel: { @@ -107,4 +109,42 @@ describe('SequelizeFolderRepository', () => { ); }); }); + + describe('findByParentUuid', () => { + const parentUuid = v4(); + const plainNames = ['Document', 'Image']; + + it('When folders are searched with names, then it should handle the call with names', async () => { + await repository.findByParentUuid(parentUuid, { + plainName: plainNames, + deleted: false, + removed: false, + }); + + expect(folderModel.findAll).toHaveBeenCalledWith({ + where: { + parentUuid: parentUuid, + plainName: { [Op.in]: plainNames }, + deleted: false, + removed: false, + }, + }); + }); + + it('When called without specific criteria, then it should handle the call', async () => { + await repository.findByParentUuid(parentUuid, { + plainName: [], + deleted: false, + removed: false, + }); + + expect(folderModel.findAll).toHaveBeenCalledWith({ + where: { + parentUuid: parentUuid, + deleted: false, + removed: false, + }, + }); + }); + }); }); diff --git a/src/modules/folder/folder.repository.ts b/src/modules/folder/folder.repository.ts index 1b3b6502f..29553d758 100644 --- a/src/modules/folder/folder.repository.ts +++ b/src/modules/folder/folder.repository.ts @@ -1,6 +1,6 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/sequelize'; -import { FindOptions, Op, Sequelize } from 'sequelize'; +import { FindOptions, Op, Sequelize, WhereOptions } from 'sequelize'; import { v4 } from 'uuid'; import { Folder } from './folder.domain'; @@ -51,6 +51,14 @@ export interface FolderRepository { folderUuid: FolderAttributes['uuid'], deleted: FolderAttributes['deleted'], ): Promise; + findByParentUuid( + parentUuid: Folder['uuid'], + searchBy: { + plainName: Folder['plainName'][]; + deleted: boolean; + removed: boolean; + }, + ): Promise; findByNameAndParentUuid( name: FolderAttributes['name'], plainName: FolderAttributes['plainName'], @@ -124,6 +132,31 @@ export class SequelizeFolderRepository implements FolderRepository { return newOrder; } + async findByParentUuid( + parentUuid: Folder['uuid'], + searchBy: { + plainName: Folder['plainName'][]; + deleted: boolean; + removed: boolean; + }, + ): Promise { + const where: WhereOptions = { + parentUuid, + removed: searchBy.removed, + deleted: searchBy.deleted, + }; + + if (searchBy && searchBy.plainName.length > 0) { + where.plainName = { [Op.in]: searchBy.plainName }; + } + + const folders = await this.folderModel.findAll({ + where, + }); + + return folders.map(this.toDomain.bind(this)); + } + async findAllCursor( where: Partial>, limit: number, diff --git a/src/modules/folder/folder.usecase.spec.ts b/src/modules/folder/folder.usecase.spec.ts index 416484bdd..f2fe0070e 100644 --- a/src/modules/folder/folder.usecase.spec.ts +++ b/src/modules/folder/folder.usecase.spec.ts @@ -994,6 +994,77 @@ describe('FolderUseCases', () => { }); }); + describe('searchFoldersInFolder', () => { + const user = newUser(); + const folderUuid = v4(); + const plainNames = ['Documents', 'Photos']; + + it('When the parent folder is not valid, then it should throw', async () => { + jest.spyOn(folderRepository, 'findOne').mockResolvedValue(null); + + await expect( + service.searchFoldersInFolder(user, folderUuid, { plainNames }), + ).rejects.toThrow(BadRequestException); + }); + + it('When folders match the specified plainNames, then it should return the folders', async () => { + const mockParentFolder = newFolder({ + attributes: { uuid: folderUuid, userId: user.id, plainName: 'Root' }, + }); + + const mockFolders = [ + newFolder({ attributes: { plainName: 'Documents', userId: user.id } }), + newFolder({ attributes: { plainName: 'Photos', userId: user.id } }), + ]; + + jest + .spyOn(folderRepository, 'findOne') + .mockResolvedValue(mockParentFolder); + jest + .spyOn(folderRepository, 'findByParentUuid') + .mockResolvedValue(mockFolders); + + const result = await service.searchFoldersInFolder(user, folderUuid, { + plainNames, + }); + + expect(result).toEqual(mockFolders); + expect(folderRepository.findByParentUuid).toHaveBeenCalledWith( + mockParentFolder.uuid, + { + plainName: plainNames, + removed: false, + deleted: false, + }, + ); + }); + + it('When no folders match the specified plainNames, then it should return an empty array', async () => { + const mockParentFolder = newFolder({ + attributes: { uuid: folderUuid, userId: user.id, plainName: 'Root' }, + }); + + jest + .spyOn(folderRepository, 'findOne') + .mockResolvedValue(mockParentFolder); + jest.spyOn(folderRepository, 'findByParentUuid').mockResolvedValue([]); + + const result = await service.searchFoldersInFolder(user, folderUuid, { + plainNames, + }); + + expect(result).toEqual([]); + expect(folderRepository.findByParentUuid).toHaveBeenCalledWith( + mockParentFolder.uuid, + { + plainName: plainNames, + removed: false, + deleted: false, + }, + ); + }); + }); + describe('getFoldersInWorkspace', () => { const createdBy = userMocked.uuid; const workspace = newWorkspace(); diff --git a/src/modules/folder/folder.usecase.ts b/src/modules/folder/folder.usecase.ts index fcc162385..bc4d22f80 100644 --- a/src/modules/folder/folder.usecase.ts +++ b/src/modules/folder/folder.usecase.ts @@ -531,6 +531,29 @@ export class FolderUseCases { ); } + async searchFoldersInFolder( + user: User, + folderUuid: Folder['uuid'], + { plainNames }: { plainNames: Folder['plainName'][] }, + ): Promise { + const parentFolder = await this.folderRepository.findOne({ + userId: user.id, + uuid: folderUuid, + removed: false, + deleted: false, + }); + + if (!parentFolder) { + throw new BadRequestException('Folder not valid!'); + } + + return this.folderRepository.findByParentUuid(parentFolder.uuid, { + plainName: plainNames, + deleted: false, + removed: false, + }); + } + getFoldersUpdatedAfter( userId: UserAttributes['id'], where: Partial,