Skip to content

Commit 02d0fa7

Browse files
committed
feat: include version sizes in user storage usage calculation
1 parent 926e764 commit 02d0fa7

8 files changed

Lines changed: 203 additions & 65 deletions

src/modules/file/file-version.domain.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ describe('FileVersion Domain', () => {
88
const mockAttributes: FileVersionAttributes = {
99
id: 'version-id-123',
1010
fileId: 'file-id-456',
11+
userId: 123,
1112
networkFileId: 'network-file-id-789',
1213
size: BigInt(1024),
1314
status: FileVersionStatus.EXISTS,
@@ -63,6 +64,7 @@ describe('FileVersion Domain', () => {
6364
expect(json).toEqual({
6465
id: mockAttributes.id,
6566
fileId: mockAttributes.fileId,
67+
userId: mockAttributes.userId,
6668
networkFileId: mockAttributes.networkFileId,
6769
size: mockAttributes.size,
6870
status: mockAttributes.status,

src/modules/file/file-version.domain.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export enum FileVersionStatus {
66
export interface FileVersionAttributes {
77
id: string;
88
fileId: string;
9+
userId: number;
910
networkFileId: string;
1011
size: bigint;
1112
status: FileVersionStatus;
@@ -16,6 +17,7 @@ export interface FileVersionAttributes {
1617
export class FileVersion implements FileVersionAttributes {
1718
id: string;
1819
fileId: string;
20+
userId: number;
1921
networkFileId: string;
2022
size: bigint;
2123
status: FileVersionStatus;
@@ -25,6 +27,7 @@ export class FileVersion implements FileVersionAttributes {
2527
private constructor(attributes: FileVersionAttributes) {
2628
this.id = attributes.id;
2729
this.fileId = attributes.fileId;
30+
this.userId = attributes.userId;
2831
this.networkFileId = attributes.networkFileId;
2932
this.size = attributes.size;
3033
this.status = attributes.status;
@@ -48,6 +51,7 @@ export class FileVersion implements FileVersionAttributes {
4851
return {
4952
id: this.id,
5053
fileId: this.fileId,
54+
userId: this.userId,
5155
networkFileId: this.networkFileId,
5256
size: this.size,
5357
status: this.status,

src/modules/file/file-version.model.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Table,
1010
} from 'sequelize-typescript';
1111
import { FileModel } from './file.model';
12+
import { UserModel } from '../user/user.model';
1213
import {
1314
FileVersionAttributes,
1415
FileVersionStatus,
@@ -33,6 +34,13 @@ export class FileVersionModel extends Model implements FileVersionAttributes {
3334
@BelongsTo(() => FileModel, 'fileId')
3435
file: FileModel;
3536

37+
@ForeignKey(() => UserModel)
38+
@Column(DataType.INTEGER)
39+
userId: number;
40+
41+
@BelongsTo(() => UserModel, 'userId')
42+
user: UserModel;
43+
3644
@Column(DataType.STRING)
3745
networkFileId: string;
3846

src/modules/file/file-version.repository.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('SequelizeFileVersionRepository', () => {
2929

3030
const result = await repository.create({
3131
fileId: version.fileId,
32+
userId: version.userId,
3233
networkFileId: version.networkFileId,
3334
size: version.size,
3435
status: version.status,
@@ -37,6 +38,7 @@ describe('SequelizeFileVersionRepository', () => {
3738
expect(result).toBeInstanceOf(FileVersion);
3839
expect(fileVersionModel.create).toHaveBeenCalledWith({
3940
fileId: version.fileId,
41+
userId: version.userId,
4042
networkFileId: version.networkFileId,
4143
size: version.size,
4244
status: version.status,
@@ -53,6 +55,7 @@ describe('SequelizeFileVersionRepository', () => {
5355

5456
await repository.create({
5557
fileId: version.fileId,
58+
userId: version.userId,
5659
networkFileId: version.networkFileId,
5760
size: version.size,
5861
} as any);
@@ -76,6 +79,7 @@ describe('SequelizeFileVersionRepository', () => {
7679

7780
const result = await repository.create({
7881
fileId: version.fileId,
82+
userId: version.userId,
7983
networkFileId: version.networkFileId,
8084
size: version.size,
8185
status: version.status,
@@ -95,6 +99,7 @@ describe('SequelizeFileVersionRepository', () => {
9599

96100
const result = await repository.create({
97101
fileId: version.fileId,
102+
userId: version.userId,
98103
networkFileId: version.networkFileId,
99104
size: version.size,
100105
status: version.status,
@@ -208,6 +213,7 @@ describe('SequelizeFileVersionRepository', () => {
208213

209214
const result = await repository.upsert({
210215
fileId: version.fileId,
216+
userId: version.userId,
211217
networkFileId: version.networkFileId,
212218
size: version.size,
213219
status: version.status,
@@ -217,6 +223,7 @@ describe('SequelizeFileVersionRepository', () => {
217223
expect(fileVersionModel.upsert).toHaveBeenCalledWith(
218224
expect.objectContaining({
219225
fileId: version.fileId,
226+
userId: version.userId,
220227
networkFileId: version.networkFileId,
221228
size: version.size,
222229
status: version.status,
@@ -235,6 +242,7 @@ describe('SequelizeFileVersionRepository', () => {
235242

236243
await repository.upsert({
237244
fileId: version.fileId,
245+
userId: version.userId,
238246
networkFileId: version.networkFileId,
239247
size: version.size,
240248
} as any);
@@ -403,4 +411,69 @@ describe('SequelizeFileVersionRepository', () => {
403411
);
404412
});
405413
});
414+
415+
describe('sumExistingSizesByUser', () => {
416+
it('When user has versions, then it returns the sum of sizes', async () => {
417+
const userId = 123;
418+
const mockResult = [{ total: '1500' }];
419+
420+
jest
421+
.spyOn(fileVersionModel, 'findAll')
422+
.mockResolvedValue(mockResult as any);
423+
424+
const result = await repository.sumExistingSizesByUser(userId);
425+
426+
expect(result).toBe(1500);
427+
expect(fileVersionModel.findAll).toHaveBeenCalledWith({
428+
attributes: expect.any(Array),
429+
where: {
430+
userId,
431+
status: FileVersionStatus.EXISTS,
432+
},
433+
raw: true,
434+
});
435+
});
436+
437+
it('When user has no versions, then it returns 0', async () => {
438+
const userId = 456;
439+
const mockResult = [{ total: null }];
440+
441+
jest
442+
.spyOn(fileVersionModel, 'findAll')
443+
.mockResolvedValue(mockResult as any);
444+
445+
const result = await repository.sumExistingSizesByUser(userId);
446+
447+
expect(result).toBe(0);
448+
});
449+
450+
it('When query returns empty array, then it returns 0', async () => {
451+
const userId = 789;
452+
453+
jest.spyOn(fileVersionModel, 'findAll').mockResolvedValue([] as any);
454+
455+
const result = await repository.sumExistingSizesByUser(userId);
456+
457+
expect(result).toBe(0);
458+
});
459+
460+
it('When summing sizes, then it only counts EXISTS status versions', async () => {
461+
const userId = 111;
462+
const mockResult = [{ total: '5000' }];
463+
464+
jest
465+
.spyOn(fileVersionModel, 'findAll')
466+
.mockResolvedValue(mockResult as any);
467+
468+
await repository.sumExistingSizesByUser(userId);
469+
470+
expect(fileVersionModel.findAll).toHaveBeenCalledWith(
471+
expect.objectContaining({
472+
where: expect.objectContaining({
473+
status: FileVersionStatus.EXISTS,
474+
}),
475+
}),
476+
);
477+
});
478+
});
406479
});

src/modules/file/file-version.repository.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Injectable } from '@nestjs/common';
22
import { InjectModel } from '@nestjs/sequelize';
3+
import { Sequelize } from 'sequelize';
34
import { FileVersionModel } from './file-version.model';
45
import {
56
FileVersion,
@@ -20,6 +21,7 @@ export interface FileVersionRepository {
2021
updateStatus(id: string, status: FileVersionStatus): Promise<void>;
2122
updateStatusBatch(ids: string[], status: FileVersionStatus): Promise<void>;
2223
deleteAllByFileId(fileId: string): Promise<void>;
24+
sumExistingSizesByUser(userId: number): Promise<number>;
2325
}
2426

2527
@Injectable()
@@ -32,6 +34,7 @@ export class SequelizeFileVersionRepository implements FileVersionRepository {
3234
async create(version: CreateFileVersionData): Promise<FileVersion> {
3335
const createdVersion = await this.model.create({
3436
fileId: version.fileId,
37+
userId: version.userId,
3538
networkFileId: version.networkFileId,
3639
size: version.size,
3740
status: version.status || FileVersionStatus.EXISTS,
@@ -44,6 +47,7 @@ export class SequelizeFileVersionRepository implements FileVersionRepository {
4447
const [instance] = await this.model.upsert(
4548
{
4649
fileId: version.fileId,
50+
userId: version.userId,
4751
networkFileId: version.networkFileId,
4852
size: version.size,
4953
status: version.status || FileVersionStatus.EXISTS,
@@ -108,4 +112,17 @@ export class SequelizeFileVersionRepository implements FileVersionRepository {
108112
},
109113
);
110114
}
115+
116+
async sumExistingSizesByUser(userId: number): Promise<number> {
117+
const result = await this.model.findAll({
118+
attributes: [[Sequelize.fn('SUM', Sequelize.col('size')), 'total']],
119+
where: {
120+
userId,
121+
status: FileVersionStatus.EXISTS,
122+
},
123+
raw: true,
124+
});
125+
126+
return Number(result[0]?.['total']) || 0;
127+
}
111128
}

0 commit comments

Comments
 (0)