-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PB-845: Calculate folder size and expose it via shared & Drive
- Loading branch information
1 parent
e027d9f
commit e3a5a03
Showing
9 changed files
with
250 additions
and
24 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
migrations/20240112041245_create-calculate-folder-size-function.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
'use strict'; | ||
|
||
const functionName = 'calculate_folder_size'; | ||
|
||
/** @type {import('sequelize-cli').Migration} */ | ||
module.exports = { | ||
up: async (queryInterface) => { | ||
await queryInterface.sequelize.query(` | ||
CREATE OR REPLACE FUNCTION ${functionName}(folder_uuid_param UUID) RETURNS BIGINT AS $$ | ||
DECLARE | ||
total_size BIGINT := 0; | ||
subfolder_uuid UUID; | ||
BEGIN | ||
total_size := total_size + COALESCE( | ||
(SELECT SUM(f.size) FROM files f WHERE f.folder_uuid = folder_uuid_param), | ||
0 | ||
); | ||
FOR subfolder_uuid IN (SELECT uuid FROM folders WHERE parent_uuid = folder_uuid_param) LOOP | ||
total_size := total_size + ${functionName}(subfolder_uuid); | ||
END LOOP; | ||
RETURN total_size; | ||
END; | ||
$$ LANGUAGE plpgsql; | ||
`); | ||
}, | ||
|
||
down: async (queryInterface) => { | ||
await queryInterface.sequelize.query( | ||
`DROP FUNCTION IF EXISTS ${functionName}(UUID);`, | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,11 @@ | ||
import { HttpStatus } from '@nestjs/common'; | ||
|
||
export class BaseHttpException extends Error { | ||
private readonly _statusCode: number; | ||
private readonly _code: string; | ||
|
||
constructor( | ||
message: string, | ||
statusCode = HttpStatus.INTERNAL_SERVER_ERROR, | ||
code?: string, | ||
public readonly message: string, | ||
public readonly statusCode = HttpStatus.INTERNAL_SERVER_ERROR, | ||
public readonly code?: string, | ||
) { | ||
super(message); | ||
this.message = message; | ||
this._statusCode = statusCode; | ||
this._code = code; | ||
} | ||
|
||
get statusCode(): number { | ||
return this._statusCode; | ||
} | ||
|
||
get code(): string { | ||
return this._code; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/modules/folder/exception/calculate-folder-size-timeout.exception.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { HttpStatus } from '@nestjs/common'; | ||
import { BaseHttpException } from '../../../common/base-http.exception'; | ||
|
||
export class CalculateFolderSizeTimeoutException extends BaseHttpException { | ||
constructor( | ||
message = 'Calculate folder size timeout', | ||
code = 'CALCULATE_FOLDER_SIZE_TIMEOUT', | ||
statusCode = HttpStatus.UNPROCESSABLE_ENTITY, | ||
) { | ||
super(message, statusCode, code); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { createMock } from '@golevelup/ts-jest'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { FileUseCases } from '../file/file.usecase'; | ||
import { FolderController } from './folder.controller'; | ||
import { FolderUseCases } from './folder.usecase'; | ||
import { CalculateFolderSizeTimeoutException } from './exception/calculate-folder-size-timeout.exception'; | ||
|
||
describe('FolderController', () => { | ||
let folderController: FolderController; | ||
let folderUseCases: FolderUseCases; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [FolderController], | ||
providers: [ | ||
{ provide: FolderUseCases, useValue: createMock() }, | ||
{ provide: FileUseCases, useValue: createMock() }, | ||
], | ||
}).compile(); | ||
|
||
folderController = module.get<FolderController>(FolderController); | ||
folderUseCases = module.get<FolderUseCases>(FolderUseCases); | ||
}); | ||
|
||
describe('getFolderSize', () => { | ||
it('When getFolderSizeController is called, Then return folder size', async () => { | ||
const folderUuid = 'valid-uuid'; | ||
const expectedSize = 100; | ||
jest | ||
.spyOn(folderUseCases, 'getFolderSizeByUuid') | ||
.mockResolvedValue(expectedSize); | ||
|
||
const result = await folderController.getFolderSize(folderUuid); | ||
expect(result).toEqual({ size: expectedSize }); | ||
}); | ||
|
||
it('When getFolderSizeController throws timeout exception, Then throws CalculateFolderSizeTimeoutException instance', async () => { | ||
const folderUuid = 'valid-uuid'; | ||
|
||
jest | ||
.spyOn(folderUseCases, 'getFolderSizeByUuid') | ||
.mockRejectedValue(new CalculateFolderSizeTimeoutException()); | ||
|
||
await expect(folderController.getFolderSize(folderUuid)).rejects.toThrow( | ||
CalculateFolderSizeTimeoutException, | ||
); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { createMock } from '@golevelup/ts-jest'; | ||
import { CalculateFolderSizeTimeoutException } from './exception/calculate-folder-size-timeout.exception'; | ||
import { SequelizeFolderRepository } from './folder.repository'; | ||
import { FolderModel } from './folder.model'; | ||
|
||
jest.mock('./folder.model', () => ({ | ||
FolderModel: { | ||
sequelize: { | ||
query: jest.fn(() => Promise.resolve([[{ totalsize: 100 }]])), | ||
}, | ||
}, | ||
})); | ||
|
||
describe('SequelizeFolderRepository', () => { | ||
let repository: SequelizeFolderRepository; | ||
let folderModel: typeof FolderModel; | ||
|
||
beforeEach(async () => { | ||
folderModel = createMock<typeof FolderModel>(); | ||
|
||
repository = new SequelizeFolderRepository(folderModel); | ||
}); | ||
|
||
describe('calculateFolderSize', () => { | ||
const folderUuid = 'your-folder-uuid'; | ||
const timeoutSeconds = 15; | ||
|
||
it('When calculateFolderSize is called, Then call the query correctly', async () => { | ||
const calculateSizeQuery = ` | ||
SET LOCAL statement_timeout = :timeoutSeconds; | ||
SELECT calculate_folder_size(:folderUuid) AS totalSize; | ||
`; | ||
|
||
jest | ||
.spyOn(FolderModel.sequelize, 'query') | ||
.mockResolvedValue([[{ totalsize: 100 }]] as any); | ||
|
||
const size = await repository.calculateFolderSize( | ||
folderUuid, | ||
timeoutSeconds, | ||
); | ||
|
||
expect(size).toBeGreaterThanOrEqual(0); | ||
expect(FolderModel.sequelize.query).toHaveBeenCalledWith( | ||
calculateSizeQuery, | ||
{ | ||
replacements: { | ||
folderUuid, | ||
timeoutSeconds: timeoutSeconds * 1000, | ||
}, | ||
}, | ||
); | ||
}); | ||
|
||
it('When calculateFolderSize query throws a timeout error, Then throw CalculateFolderSizeTimeoutException', async () => { | ||
jest.spyOn(FolderModel.sequelize, 'query').mockRejectedValue({ | ||
original: { | ||
code: '57014', | ||
}, | ||
}); | ||
|
||
await expect( | ||
repository.calculateFolderSize(folderUuid, timeoutSeconds), | ||
).rejects.toThrow(CalculateFolderSizeTimeoutException); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters