Skip to content

Commit 66dfb48

Browse files
authored
fix: add limit 1 to backup queries instead of fetching all items in a folder (#1039)
1 parent e876e86 commit 66dfb48

6 files changed

Lines changed: 59 additions & 127 deletions

File tree

src/modules/backups/backup.usecase.spec.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { SequelizeBackupRepository } from './backup.repository';
66
import { BridgeService } from '../../externals/bridge/bridge.service';
77
import { CryptoService } from '../../externals/crypto/crypto.service';
88
import { FolderUseCases } from '../folder/folder.usecase';
9-
import { FileUseCases } from '../file/file.usecase';
109
import {
1110
NotFoundException,
1211
ConflictException,
@@ -15,16 +14,17 @@ import {
1514
import { type Folder } from '../folder/folder.domain';
1615
import { DevicePlatform } from './device.domain';
1716
import { SequelizeFolderRepository } from '../folder/folder.repository';
17+
import { SequelizeFileRepository } from '../file/file.repository';
1818

1919
describe('BackupUseCase', () => {
2020
let backupUseCase: BackupUseCase;
2121
let backupRepository: SequelizeBackupRepository;
2222
let folderRepository: SequelizeFolderRepository;
23+
let fileRepository: SequelizeFileRepository;
2324

2425
let bridgeService: BridgeService;
2526
let cryptoService: CryptoService;
2627
let folderUseCases: FolderUseCases;
27-
let fileUseCases: FileUseCases;
2828

2929
const userMocked = newUser({
3030
attributes: {
@@ -55,10 +55,12 @@ describe('BackupUseCase', () => {
5555
bridgeService = module.get<BridgeService>(BridgeService);
5656
cryptoService = module.get<CryptoService>(CryptoService);
5757
folderUseCases = module.get<FolderUseCases>(FolderUseCases);
58-
fileUseCases = module.get<FileUseCases>(FileUseCases);
5958
folderRepository = module.get<SequelizeFolderRepository>(
6059
SequelizeFolderRepository,
6160
);
61+
fileRepository = module.get<SequelizeFileRepository>(
62+
SequelizeFileRepository,
63+
);
6264
});
6365

6466
describe('activate', () => {
@@ -81,9 +83,8 @@ describe('BackupUseCase', () => {
8183

8284
describe('createDeviceAsFolder', () => {
8385
it('When a folder with the same name exists, then it should throw a ConflictException', async () => {
84-
jest
85-
.spyOn(folderUseCases, 'getFolders')
86-
.mockResolvedValue([{ id: 1, name: 'Device Folder' }] as any);
86+
const existentFolder = newFolder();
87+
jest.spyOn(folderRepository, 'findOne').mockResolvedValue(existentFolder);
8788

8889
await expect(
8990
backupUseCase.createDeviceAsFolder(userMocked, 'Device Folder'),
@@ -92,10 +93,11 @@ describe('BackupUseCase', () => {
9293

9394
it('When no folder with the same name exists, then it should create the folder', async () => {
9495
const mockFolder = newFolder();
95-
jest.spyOn(folderUseCases, 'getFolders').mockResolvedValue([]);
96+
jest.spyOn(folderRepository, 'findOne').mockResolvedValue(null);
9697
jest
97-
.spyOn(folderUseCases, 'createFolderDevice')
98+
.spyOn(folderRepository, 'createFolder')
9899
.mockResolvedValue(mockFolder);
100+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
99101

100102
const result = await backupUseCase.createDeviceAsFolder(
101103
userMocked,
@@ -123,6 +125,7 @@ describe('BackupUseCase', () => {
123125
jest
124126
.spyOn(folderUseCases, 'getFoldersByUserId')
125127
.mockResolvedValue([mockFolder]);
128+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
126129

127130
const result = await backupUseCase.getDevicesAsFolder(userMocked);
128131

@@ -189,10 +192,9 @@ describe('BackupUseCase', () => {
189192
describe('isFolderEmpty', () => {
190193
it('When the folder has no subfolders or files, then it should return true', async () => {
191194
const parentFolder = newFolder();
192-
jest
193-
.spyOn(folderUseCases, 'getFoldersByParentUuid')
194-
.mockResolvedValue([]);
195-
jest.spyOn(fileUseCases, 'getByFolderAndUser').mockResolvedValue([]);
195+
196+
jest.spyOn(folderRepository, 'findOne').mockResolvedValue(null);
197+
jest.spyOn(fileRepository, 'findOneBy').mockResolvedValue(null);
196198

197199
const result = await backupUseCase.isFolderEmpty(
198200
userMocked,
@@ -203,12 +205,9 @@ describe('BackupUseCase', () => {
203205

204206
it('When the folder has subfolders or files, then it should return false', async () => {
205207
const parentFolder = newFolder();
206-
const mockFolder = newFolder();
207-
208-
jest
209-
.spyOn(folderUseCases, 'getFoldersByParentUuid')
210-
.mockResolvedValue([mockFolder]);
211-
jest.spyOn(fileUseCases, 'getByFolderAndUser').mockResolvedValue([]);
208+
const folderInBackup = newFolder();
209+
jest.spyOn(folderRepository, 'findOne').mockResolvedValue(folderInBackup);
210+
jest.spyOn(fileRepository, 'findOneBy').mockResolvedValue(null);
212211

213212
const result = await backupUseCase.isFolderEmpty(
214213
userMocked,
@@ -354,6 +353,7 @@ describe('BackupUseCase', () => {
354353
jest
355354
.spyOn(folderUseCases, 'updateByFolderIdAndForceUpdatedAt')
356355
.mockResolvedValue(updatedFolder);
356+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
357357

358358
const result = await backupUseCase.updateDeviceAsFolder(
359359
userMocked,
@@ -570,6 +570,7 @@ describe('BackupUseCase', () => {
570570
jest
571571
.spyOn(folderRepository, 'findByUuids')
572572
.mockResolvedValue([mockFolder]);
573+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
573574

574575
const result = await backupUseCase.getUserDevices(userMocked, {}, 10, 0);
575576

@@ -837,6 +838,7 @@ describe('BackupUseCase', () => {
837838
jest
838839
.spyOn(backupRepository, 'createDevice')
839840
.mockResolvedValue(mockDevice);
841+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
840842

841843
const result = await backupUseCase.createDeviceForExistingFolder(
842844
userMocked,
@@ -1078,6 +1080,7 @@ describe('BackupUseCase', () => {
10781080
jest
10791081
.spyOn(folderRepository, 'updateOneAndReturn')
10801082
.mockResolvedValue(mockFolder);
1083+
jest.spyOn(backupUseCase, 'isFolderEmpty').mockResolvedValue(true);
10811084

10821085
jest
10831086
.spyOn(backupRepository, 'updateDeviceName')

src/modules/backups/backup.usecase.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { type User } from '../user/user.domain';
1111
import { BridgeService } from './../../externals/bridge/bridge.service';
1212
import { CryptoService } from './../../externals/crypto/crypto.service';
1313
import { FolderUseCases } from '../folder/folder.usecase';
14-
import { FileUseCases } from '../file/file.usecase';
1514
import { Folder, type FolderAttributes } from '../folder/folder.domain';
1615
import { SequelizeUserRepository } from '../user/user.repository';
1716
import { type BackupModel } from './models/backup.model';
@@ -21,6 +20,8 @@ import { type CreateDeviceAndAttachFolderDto } from './dto/create-device-and-att
2120
import { type DevicePlatform } from './device.domain';
2221
import { type UpdateDeviceAndFolderDto } from './dto/update-device-and-folder.dto';
2322
import { SequelizeFolderRepository } from '../folder/folder.repository';
23+
import { SequelizeFileRepository } from '../file/file.repository';
24+
import { FileStatus } from '../file/file.domain';
2425

2526
@Injectable()
2627
export class BackupUseCase {
@@ -32,8 +33,7 @@ export class BackupUseCase {
3233
@Inject(forwardRef(() => FolderUseCases))
3334
private readonly folderUsecases: FolderUseCases,
3435
private readonly folderRepository: SequelizeFolderRepository,
35-
@Inject(forwardRef(() => FileUseCases))
36-
private readonly fileUsecases: FileUseCases,
36+
private readonly fileRepository: SequelizeFileRepository,
3737
) {}
3838

3939
async deleteUserBackups(userId: number) {
@@ -49,6 +49,7 @@ export class BackupUseCase {
4949
return this.backupRepository.findAllLegacyDevices(user);
5050
}
5151

52+
// @deprecated
5253
async deleteDevice(user: User, deviceId: number) {
5354
const device = await this.backupRepository.findDeviceByUserAndId(
5455
user,
@@ -97,18 +98,20 @@ export class BackupUseCase {
9798
bucket = backupsBucket;
9899
}
99100

100-
const folders = await this.folderUsecases.getFolders(user.id, {
101+
// We do not have an index to cover this query, but it is not a frequent operation
102+
const folder = await this.folderRepository.findOne({
101103
bucket,
102104
plainName: deviceName,
103105
deleted: false,
104106
removed: false,
107+
userId: user.id,
105108
});
106109

107-
if (folders.length > 0) {
110+
if (folder) {
108111
throw new ConflictException('Folder with the same name already exists');
109112
}
110113

111-
const createdFolder = await this.folderUsecases.createFolderDevice(user, {
114+
const createdFolder = await this.folderRepository.createFolder(user.id, {
112115
plainName: deviceName,
113116
bucket,
114117
});
@@ -483,16 +486,19 @@ export class BackupUseCase {
483486
}
484487

485488
async isFolderEmpty(user: User, folder: Folder) {
486-
const folders = await this.folderUsecases.getFoldersByParentUuid(
487-
folder.uuid,
488-
user.id,
489-
);
490-
const files = await this.fileUsecases.getByFolderAndUser(
491-
folder.id,
492-
user.id,
493-
{ deleted: false },
494-
);
495-
return folders.length === 0 && files.length === 0;
489+
const folderInFolder = await this.folderRepository.findOne({
490+
parentUuid: folder.uuid,
491+
userId: user.id,
492+
removed: false,
493+
deleted: false,
494+
});
495+
const fileInFolder = await this.fileRepository.findOneBy({
496+
folderUuid: folder.uuid,
497+
userId: user.id,
498+
status: FileStatus.EXISTS,
499+
});
500+
501+
return !folderInFolder && !fileInFolder;
496502
}
497503

498504
async sumExistentBackupSizes(userId: number) {

src/modules/file/file.usecase.spec.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3313,25 +3313,6 @@ describe('FileUseCases', () => {
33133313
});
33143314
});
33153315

3316-
describe('getFilesByFolderUuid', () => {
3317-
it('When called with folder uuid and status, then it should return files', async () => {
3318-
const folderUuid = v4();
3319-
const status = FileStatus.EXISTS;
3320-
const mockFiles = [newFile()];
3321-
jest
3322-
.spyOn(fileRepository, 'getFilesByFolderUuid')
3323-
.mockResolvedValue(mockFiles);
3324-
3325-
const result = await service.getFilesByFolderUuid(folderUuid, status);
3326-
3327-
expect(result).toEqual(mockFiles);
3328-
expect(fileRepository.getFilesByFolderUuid).toHaveBeenCalledWith(
3329-
folderUuid,
3330-
status,
3331-
);
3332-
});
3333-
});
3334-
33353316
describe('hasUploadedFiles', () => {
33363317
it('When user has uploaded files, then it should return true', async () => {
33373318
const mockFile = newFile();

src/modules/file/file.usecase.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -490,13 +490,6 @@ export class FileUseCases {
490490
};
491491
}
492492

493-
async getFilesByFolderUuid(
494-
folderUuid: FileAttributes['folderUuid'],
495-
status: FileStatus,
496-
) {
497-
return this.fileRepository.getFilesByFolderUuid(folderUuid, status);
498-
}
499-
500493
getFilesByIds(user: User, fileIds: File['id'][]): Promise<File[]> {
501494
return this.fileRepository.findByIds(user.id, fileIds);
502495
}

0 commit comments

Comments
 (0)