diff --git a/bin/cli/init.ts b/bin/cli/init.ts index b27f2ad5..bf3122c2 100644 --- a/bin/cli/init.ts +++ b/bin/cli/init.ts @@ -12,6 +12,7 @@ import { MongoDBBucketEntryShardsRepository } from '../../lib/core/bucketEntrySh import { MongoDBUploadsRepository } from '../../lib/core/uploads/MongoDBUploadsRepository'; import { MongoDBTokensRepository } from '../../lib/core/tokens/MongoDBTokensRepository'; import { MongoDBContactsRepository } from '../../lib/core/contacts/MongoDBContactsRepository'; +import { MongoDBFileStateRepository } from '../../lib/core/fileState/MongoDBFileStateRepository'; import { BucketEntriesUsecase } from '../../lib/core/bucketEntries/usecase'; import { ShardsUsecase } from '../../lib/core/shards/usecase'; @@ -30,6 +31,7 @@ import { TokensRepository } from '../../lib/core/tokens/Repository'; import { ContactsRepository } from '../../lib/core/contacts/Repository'; import { MongoDB } from '../delete-objects/temp-shard.model'; import { DatabaseFramesReader, DatabaseBucketEntriesReaderWithoutBucket } from '../delete-objects/ObjectStorage'; +import { FileStateRepository } from '../../lib/core/fileState/Repository'; const Config = require('../../lib/config'); @@ -42,7 +44,7 @@ export type PrepareFunctionReturnType = { }, repo: { bucketEntriesRepository: BucketEntriesRepository, - bucketEntryShardsRepository:BucketEntryShardsRepository, + bucketEntryShardsRepository: BucketEntryShardsRepository, bucketsRepository: BucketsRepository, usersRepository: UsersRepository, framesRepository: FramesRepository, @@ -94,6 +96,7 @@ export async function prepare(): Promise { const uploadsRepository = new MongoDBUploadsRepository(models.Upload); const tokensRepository = new MongoDBTokensRepository(models.Token); const contactsRepository = new MongoDBContactsRepository(models.Contact); + const fileStateRepository: FileStateRepository = new MongoDBFileStateRepository(models.FileState); const shardsUsecase = new ShardsUsecase( mirrorsRepository, @@ -109,7 +112,8 @@ export async function prepare(): Promise { pointersRepository, mirrorsRepository, shardsUsecase, - usersRepository + usersRepository, + fileStateRepository ); const bucketsUsecase = new BucketsUsecase( bucketEntryShardsRepository, @@ -117,7 +121,7 @@ export async function prepare(): Promise { mirrorsRepository, framesRepository, shardsRepository, - bucketsRepository, + bucketsRepository, uploadsRepository, usersRepository, tokensRepository, diff --git a/bin/utils/database.ts b/bin/utils/database.ts index 7861306e..958dfe5a 100644 --- a/bin/utils/database.ts +++ b/bin/utils/database.ts @@ -7,31 +7,32 @@ const Storage = require('storj-service-storage-models') as any; const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -export interface Models { - BucketEntry: any, - BucketEntryShard: any, - Bucket: any, - Shard: any, +export interface Models { + BucketEntry: any, + BucketEntryShard: any, + Bucket: any, + Shard: any, Mirror: any, User: any, Frame: any, Pointer: any, Upload: any, Token: any, - Contact: any + Contact: any, + FileState: any, }; export async function connectToDatabase(configJSON: any, mongoURL: string): Promise { const config = new Config(process.env.NODE_ENV, configJSON, '') as { - storage: { - mongoUrl: string; - mongoOpts: any + storage: { + mongoUrl: string; + mongoOpts: any }, QUEUE_USERNAME: string; QUEUE_PASSWORD: string; QUEUE_HOST: string; }; - + const storage = new Storage( mongoURL || config.storage.mongoUrl, config.storage.mongoOpts, diff --git a/lib/core/bucketEntries/usecase.ts b/lib/core/bucketEntries/usecase.ts index ec99922f..733a3263 100644 --- a/lib/core/bucketEntries/usecase.ts +++ b/lib/core/bucketEntries/usecase.ts @@ -13,6 +13,7 @@ import { BucketEntry } from './BucketEntry'; import { UsersRepository } from '../users/Repository'; import { User } from '../users/User'; import { Bucket } from '../buckets/Bucket'; +import { FileStateRepository } from '../fileState/Repository'; export class BucketEntryVersionNotFoundError extends Error { constructor() { @@ -32,10 +33,11 @@ export class BucketEntriesUsecase { private pointersRepository: PointersRepository, private mirrorsRepository: MirrorsRepository, private shardsUsecase: ShardsUsecase, - private usersRepository: UsersRepository + private usersRepository: UsersRepository, + private fileStateRepository: FileStateRepository ) { } - async listByBucket(bucketId: Bucket['id'], limit=20, offset=0): Promise { + async listByBucket(bucketId: Bucket['id'], limit = 20, offset = 0): Promise { const bucketEntries = await this.bucketEntriesRepository.findByBucket(bucketId, limit, offset); return bucketEntries; @@ -56,7 +58,7 @@ export class BucketEntriesUsecase { async removeFileFromUser(bucketId: string, fileId: string, userId: User['uuid']) { const bucket = await this.bucketsRepository.findOne({ id: bucketId }); - if(!bucket) { + if (!bucket) { throw new BucketNotFoundError(); } @@ -77,9 +79,9 @@ export class BucketEntriesUsecase { const version = bucketEntry.version; if (!version || version === 1) { - await this.removeFilesV1([ bucketEntry ]); + await this.removeFilesV1([bucketEntry]); } else if (version === 2) { - await this.removeFilesV2([ bucketEntry ]); + await this.removeFilesV2([bucketEntry]); const bucket = await this.bucketsRepository.findOne({ id: bucketEntry.bucket }); if (bucket?.userId) { @@ -98,7 +100,7 @@ export class BucketEntriesUsecase { const bucketEntries = await this.bucketEntriesRepository.findByIds(fileIds); const bucketEntriesV2 = bucketEntries.filter(b => b.version && b.version === 2); const bucketEntriesV1 = bucketEntries.filter(b => !b.version || b.version === 1); - + if (bucketEntriesV1.length > 0) { await this.removeFilesV1(bucketEntriesV1); } @@ -115,7 +117,7 @@ export class BucketEntriesUsecase { Object.keys(bucketsGroupedByUsers).forEach((userId) => { storageToModifyPerUser[userId] = 0; }); - + Object.keys(bucketsGroupedByUsers).forEach((userId) => { const buckets = bucketsGroupedByUsers[userId]; @@ -130,8 +132,8 @@ export class BucketEntriesUsecase { await this.usersRepository.addTotalUsedSpaceBytes(user, storageToSubstract); } - } - + } + return fileIds; } @@ -146,7 +148,7 @@ export class BucketEntriesUsecase { if (shardsHashes.length > 0) { await this.shardsUsecase.deleteShardsStorageByHashes(shardsHashes); - await this.shardsRepository.deleteByHashes(shardsHashes); + await this.shardsRepository.deleteByHashes(shardsHashes); } if (pointerIds.length > 0) { @@ -156,7 +158,7 @@ export class BucketEntriesUsecase { if (frames.length > 0) { await this.framesRepository.deleteByIds(frames.map(f => f.id)); } - + await this.bucketEntriesRepository.deleteByIds(files.map(f => f.id)); } @@ -169,13 +171,14 @@ export class BucketEntriesUsecase { if (shards.length > 0) { await this.shardsUsecase.deleteShardsStorageByUuids(shards as any); - await this.shardsRepository.deleteByIds(shards.map(s => s.id)); + await this.shardsRepository.deleteByIds(shards.map(s => s.id)); } if (bucketEntryShardsIds.length > 0) { await this.bucketEntryShardsRepository.deleteByIds(bucketEntryShardsIds); } - + await this.bucketEntriesRepository.deleteByIds(fileIds); + await this.fileStateRepository.deleteByBucketEntryIds(fileIds); } } diff --git a/lib/core/fileState/FileState.ts b/lib/core/fileState/FileState.ts new file mode 100644 index 00000000..8c4aff6c --- /dev/null +++ b/lib/core/fileState/FileState.ts @@ -0,0 +1,4 @@ +export interface FileState { + bucketEntry: string; + lastAccessDate: string; +} diff --git a/lib/core/fileState/MongoDBFileStateRepository.ts b/lib/core/fileState/MongoDBFileStateRepository.ts new file mode 100644 index 00000000..8c85e184 --- /dev/null +++ b/lib/core/fileState/MongoDBFileStateRepository.ts @@ -0,0 +1,30 @@ +import { Model } from 'mongoose'; +import { FileState } from './FileState'; +import { FileStateRepository } from './Repository'; + + +export class MongoDBFileStateRepository implements FileStateRepository { + constructor(private model: Model) { } + + async findByBucketEntry(bucketEntry: FileState['bucketEntry']): Promise { + const fileState = await this.model.findOne({ bucketEntry }); + + if (!fileState) { + return null; + } + + return fileState; + } + + async setLastAccessDate(bucketEntryId: FileState['bucketEntry'], accessDate = new Date()): Promise { + return this.model.findOneAndUpdate( + { bucketEntry: bucketEntryId }, + { $set: { lastAccessDate: accessDate } }, + { new: true, upsert: true } + ); + } + + async deleteByBucketEntryIds(ids: FileState['bucketEntry'][]): Promise { + await this.model.deleteMany({ bucketEntry: { $in: ids } }); + } +} diff --git a/lib/core/fileState/Repository.ts b/lib/core/fileState/Repository.ts new file mode 100644 index 00000000..25119ea6 --- /dev/null +++ b/lib/core/fileState/Repository.ts @@ -0,0 +1,6 @@ +import { FileState } from "./FileState"; + +export interface FileStateRepository { + setLastAccessDate(bucketEntryId: FileState['bucketEntry'], accessDate?: Date): Promise + deleteByBucketEntryIds(ids: FileState['bucketEntry'][]): Promise +} diff --git a/lib/core/fileState/usecase.ts b/lib/core/fileState/usecase.ts new file mode 100644 index 00000000..1cd6b0a3 --- /dev/null +++ b/lib/core/fileState/usecase.ts @@ -0,0 +1,17 @@ +import { FileStateRepository } from './Repository'; + +export class FileStateUsecase { + + constructor( + private fileStateRepository: FileStateRepository, + ) { } + + async updateOrSetLastAccessDate(bucketEntryId: string) { + const fileState = await this.fileStateRepository.setLastAccessDate(bucketEntryId); + return fileState + } + + async removeFileStateByEntryId(bucketEntryId: string) { + await this.fileStateRepository.deleteByBucketEntryIds([bucketEntryId]); + } +} diff --git a/lib/server/http/index.ts b/lib/server/http/index.ts index 27092b6d..43f4c914 100644 --- a/lib/server/http/index.ts +++ b/lib/server/http/index.ts @@ -31,6 +31,8 @@ import { ShardsUsecase } from "../../core/shards/usecase"; import { BucketEntryShardsRepository } from "../../core/bucketEntryShards/Repository"; import { MongoDBBucketEntryShardsRepository } from "../../core/bucketEntryShards/MongoDBBucketEntryShardsRepository"; import { Notifications } from "../notifications"; +import { FileStateRepository } from "../../core/fileState/Repository"; +import { MongoDBFileStateRepository } from "../../core/fileState/MongoDBFileStateRepository"; const { authenticate } = require('storj-service-middleware'); @@ -44,12 +46,13 @@ interface Models { Contact: any; Shard: any; BucketEntryShard: any; + FileState: any; } export function bindNewRoutes( - app: Application, - storage: { models: Models }, - mailer: Mailer, + app: Application, + storage: { models: Models }, + mailer: Mailer, profile: Profile, log: Logger, networkQueue: any, @@ -66,14 +69,15 @@ export function bindNewRoutes( const pointersRepository: PointersRepository = new MongoDBPointersRepository(models.Pointer); const contactsRepository: ContactsRepository = new MongoDBContactsRepository(models.Contact); const shardsRepository: ShardsRepository = new MongoDBShardsRepository(models.Shard); + const fileStateRepository: FileStateRepository = new MongoDBFileStateRepository(models.FileState); const mailUsecase: MailUsecase = new SendGridMailUsecase(mailer, profile); const eventBus = new EventBus(log, mailUsecase, notifications); const usersUsecase = new UsersUsecase( - usersRepository, + usersRepository, framesRepository, - bucketsRepository, + bucketsRepository, mailUsecase, eventBus ); @@ -104,7 +108,8 @@ export function bindNewRoutes( pointersRepository, mirrorsRepository, shardsUsecase, - usersRepository + usersRepository, + fileStateRepository ); const basicAuthMiddleware = authenticate(storage); @@ -113,8 +118,8 @@ export function bindNewRoutes( const usersController = new HTTPUsersController(usersUsecase, log); const gatewayController = new HTTPGatewayController( - gatewayUsecase, - bucketEntriesUsecase, + gatewayUsecase, + bucketEntriesUsecase, usersUsecase, log, eventBus diff --git a/lib/server/routes/buckets.js b/lib/server/routes/buckets.js index a0690d79..e6d8cc71 100644 --- a/lib/server/routes/buckets.js +++ b/lib/server/routes/buckets.js @@ -35,6 +35,8 @@ const { MongoDBPointersRepository } = require('../../core/pointers/MongoDBPointe const { ShardsUsecase } = require('../../core/shards/usecase'); const { MongoDBTokensRepository } = require('../../core/tokens/MongoDBTokensRepository'); const { MongoDBContactsRepository } = require('../../core/contacts/MongoDBContactsRepository'); +const { MongoDBFileStateRepository } = require('../../core/fileState/MongoDBFileStateRepository'); +const { FileStateUsecase } = require('../../core/fileState/usecase'); /** * Handles endpoints for all bucket and file related operations @@ -66,6 +68,7 @@ function BucketsRouter(options) { const pointersRepository = new MongoDBPointersRepository(this.storage.models.Pointer); const tokensRepository = new MongoDBTokensRepository(this.storage.models.Token); const contactsRepository = new MongoDBContactsRepository(this.storage.models.Contact); + const fileStateRepository = new MongoDBFileStateRepository(this.storage.models.FileState); this.usecase = new BucketsUsecase( bucketEntryShardsRepository, @@ -95,7 +98,12 @@ function BucketsRouter(options) { pointersRepository, mirrorsRepository, this.shardsUseCase, - usersRepository + usersRepository, + fileStateRepository + ); + + this.fileStateUsecase = new FileStateUsecase( + fileStateRepository ); } @@ -1117,6 +1125,11 @@ BucketsRouter.prototype.getFileInfo = async function (req, res, next) { fileInfo.shards = fileShards; } + await this.fileStateUsecase.updateOrSetLastAccessDate(req.params.file) + .catch(error=> + log.error('getFileInfo: Error updating last access date for user %s and file %s : %s', req.user.uuid, req.params.file, error.message) + ); + return res.status(200).send(fileInfo); } catch (err) { if (err instanceof BucketEntryNotFoundError || err instanceof BucketEntryFrameNotFoundError || err instanceof BucketNotFoundError) { @@ -1263,6 +1276,11 @@ BucketsRouter.prototype.finishUpload = async function (req, res, next) { auth ); + await this.fileStateUsecase.updateOrSetLastAccessDate(bucketEntry.id) + .catch(error=> + log.error('finishUpload: Error inserting last access date for user %s and file %s : %s', req.user.uuid, bucketEntry.id, error.message) + ); + res.status(200).send(bucketEntry); } catch (err) { if (err instanceof BucketNotFoundError) { diff --git a/package.json b/package.json index 6326ae17..3a87328c 100644 --- a/package.json +++ b/package.json @@ -119,10 +119,10 @@ "redis": "^3.1.0", "secp256k1": "^4.0.2", "storj-lib": "github:internxt/core#v8.7.3-beta", - "storj-mongodb-adapter": "github:internxt/mongodb-adapter#10.0.1-beta", + "storj-mongodb-adapter": "github:internxt/mongodb-adapter#10.1.0-beta", "storj-service-error-types": "github:internxt/service-error-types", "storj-service-middleware": "github:internxt/service-middleware", - "storj-service-storage-models": "github:internxt/service-storage-models#11.0.1", + "storj-service-storage-models": "github:internxt/service-storage-models#11.1.0", "stripe": "^8.49.0", "through": "^2.3.8", "typescript": "^4.6.3", diff --git a/tests/lib/core/bucketentries/usecase.test.ts b/tests/lib/core/bucketentries/usecase.test.ts index fb5c834f..45d573e1 100644 --- a/tests/lib/core/bucketentries/usecase.test.ts +++ b/tests/lib/core/bucketentries/usecase.test.ts @@ -1,4 +1,5 @@ import { restore, stub } from 'sinon'; +import { Model } from 'mongoose'; import { BucketEntriesRepository } from '../../../../lib/core/bucketEntries/Repository'; import { FramesRepository } from '../../../../lib/core/frames/Repository'; @@ -26,11 +27,13 @@ import { BucketEntry } from '../../../../lib/core/bucketEntries/BucketEntry'; import { Bucket } from '../../../../lib/core/buckets/Bucket'; import { ContactsRepository } from '../../../../lib/core/contacts/Repository'; import { MongoDBContactsRepository } from '../../../../lib/core/contacts/MongoDBContactsRepository'; +import { FileStateRepository } from '../../../../lib/core/fileState/Repository'; +import { MongoDBFileStateRepository } from '../../../../lib/core/fileState/MongoDBFileStateRepository'; describe('BucketEntriesUsecase', function () { - const bucketId = 'bucketIdSAMPLE'; - const userEmail = 'sample@sample.com'; - const fileId = 'abc123'; + const bucketId = 'bucketIdSAMPLE'; + const userEmail = 'sample@sample.com'; + const fileId = 'abc123'; let bucketEntriesRepository: BucketEntriesRepository = new MongoDBBucketEntriesRepository({}); let mirrorsRepository: MirrorsRepository = new MongoDBMirrorsRepository({}); @@ -41,9 +44,10 @@ describe('BucketEntriesUsecase', function () { let usersRepository: UsersRepository = new MongoDBUsersRepository({}); let bucketEntryShardsRepository: BucketEntryShardsRepository = new MongoDBBucketEntryShardsRepository({}); let contactsRepository: ContactsRepository = new MongoDBContactsRepository({}); + let fileStateRepository: FileStateRepository = new MongoDBFileStateRepository({} as Model); let networkQueue: any = { - enqueueMessage: (message: any) => {} + enqueueMessage: (message: any) => { } }; let shardsUseCase = new ShardsUsecase( @@ -61,9 +65,10 @@ describe('BucketEntriesUsecase', function () { pointersRepository, mirrorsRepository, shardsUseCase, - usersRepository + usersRepository, + fileStateRepository ); - + beforeEach(() => { bucketEntriesRepository = new MongoDBBucketEntriesRepository({}); mirrorsRepository = new MongoDBMirrorsRepository({}); @@ -72,6 +77,7 @@ describe('BucketEntriesUsecase', function () { bucketsRepository = new MongoDBBucketsRepository({}); pointersRepository = new MongoDBPointersRepository({}); contactsRepository = new MongoDBContactsRepository({}); + fileStateRepository = new MongoDBFileStateRepository({} as Model); shardsUseCase = new ShardsUsecase( mirrorsRepository, @@ -88,7 +94,8 @@ describe('BucketEntriesUsecase', function () { pointersRepository, mirrorsRepository, shardsUseCase, - usersRepository + usersRepository, + fileStateRepository ); restore(); @@ -104,11 +111,11 @@ describe('BucketEntriesUsecase', function () { bucketEntriesRepository, 'deleteByIds' ); - + await bucketEntriesUsecase.removeFilesV1(bucketEntries); expect(findFramesByIdsStub.calledOnce).toBeTruthy(); - expect(findPointersByIdsStub.calledOnce).toBeTruthy(); + expect(findPointersByIdsStub.calledOnce).toBeTruthy(); expect(deleteBucketEntriesByIdsStub.calledOnce).toBeTruthy(); expect(findPointersByIdsStub.calledAfter(findFramesByIdsStub)).toBeTruthy(); @@ -178,7 +185,7 @@ describe('BucketEntriesUsecase', function () { it('Should delete pointers and shards if they exist', async () => { const bucketEntries = fixtures.getBucketEntriesWithoutFrames(); - const frames = bucketEntries.map(b => fixtures.getFrame({ id: b.frame, shards: [ fixtures.getPointer().id ] })); + const frames = bucketEntries.map(b => fixtures.getFrame({ id: b.frame, shards: [fixtures.getPointer().id] })); const pointers = frames.flatMap(f => f.shards.map(pId => fixtures.getPointer({ id: pId }))); stub(framesRepository, 'findByIds').resolves(frames); @@ -213,11 +220,14 @@ describe('BucketEntriesUsecase', function () { bucketEntriesRepository, 'deleteByIds' ); - + const deleteFileStateByEntrySub = stub(fileStateRepository, 'deleteByBucketEntryIds').resolves() + + await bucketEntriesUsecase.removeFilesV2(bucketEntries); expect(deleteBucketEntriesByIdsStub.calledOnce).toBeTruthy(); expect(deleteBucketEntriesByIdsStub.calledWith(bucketEntries.map(b => b.id))).toBeTruthy(); + expect(deleteFileStateByEntrySub.calledWith(bucketEntries.map(b => b.id))).toBeTruthy(); }); it('Should skip shards deletion if do not exist', async () => { @@ -226,7 +236,8 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntryShardsRepository, 'findByBucketEntries').resolves([]); stub(shardsRepository, 'findByIds').resolves([]); stub(bucketEntriesRepository, 'deleteByIds').resolves(); - + stub(fileStateRepository, 'deleteByBucketEntryIds').resolves() + const deleteStorageStub = stub(shardsUseCase, 'deleteShardsStorageByUuids'); const deleteShards = stub(shardsRepository, 'deleteByIds'); @@ -242,7 +253,8 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntryShardsRepository, 'findByBucketEntries').resolves([]); stub(shardsRepository, 'findByIds').resolves([]); stub(bucketEntriesRepository, 'deleteByIds').resolves() - + stub(fileStateRepository, 'deleteByBucketEntryIds').resolves() + const deleteBucketEntryShardsStub = stub(bucketEntryShardsRepository, 'deleteByIds'); await bucketEntriesUsecase.removeFilesV2(bucketEntries); @@ -257,7 +269,8 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntryShardsRepository, 'findByBucketEntries').resolves(bucketEntryShards); stub(shardsRepository, 'findByIds').resolves([]); stub(bucketEntriesRepository, 'deleteByIds').resolves() - + stub(fileStateRepository, 'deleteByBucketEntryIds').resolves() + const deleteBucketEntryShardsStub = stub(bucketEntryShardsRepository, 'deleteByIds'); await bucketEntriesUsecase.removeFilesV2(bucketEntries); @@ -277,6 +290,7 @@ describe('BucketEntriesUsecase', function () { const findShardsStub = stub(shardsRepository, 'findByIds').resolves(shards); const deleteShardsStorageStub = stub(shardsUseCase, 'deleteShardsStorageByUuids').resolves(); const deleteShardsStub = stub(shardsRepository, 'deleteByIds').resolves(); + stub(fileStateRepository, 'deleteByBucketEntryIds').resolves() await bucketEntriesUsecase.removeFilesV2(bucketEntries); @@ -314,7 +328,7 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntriesRepository, 'findOne').resolves(bucketEntry); const removeFilesV1Stub = stub(bucketEntriesUsecase, 'removeFilesV1').resolves(); - + await bucketEntriesUsecase.removeFile(fileId); expect(removeFilesV1Stub.calledOnce).toBeTruthy(); @@ -330,7 +344,7 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntriesRepository, 'findOne').resolves(bucketEntry); const removeFilesV1Stub = stub(bucketEntriesUsecase, 'removeFilesV1').resolves(); - + await bucketEntriesUsecase.removeFile(fileId); expect(removeFilesV1Stub.calledOnce).toBeTruthy(); @@ -439,7 +453,7 @@ describe('BucketEntriesUsecase', function () { stub(bucketEntriesRepository, 'findOne').resolves(bucketEntry); - try { + try { await bucketEntriesUsecase.removeFile(fileId); expect(true).toBeFalsy(); } catch (err) { @@ -466,7 +480,7 @@ describe('BucketEntriesUsecase', function () { const nonExistentFileId = 'file-id'; const emptyResult: BucketEntry[] = []; - stub(bucketEntriesRepository, 'findByIds').resolves(emptyResult); + stub(bucketEntriesRepository, 'findByIds').resolves(emptyResult); const removeFilesV1Spy = jest.spyOn(bucketEntriesUsecase, 'removeFilesV1'); const removeFilesV2Spy = jest.spyOn(bucketEntriesUsecase, 'removeFilesV2'); @@ -481,14 +495,14 @@ describe('BucketEntriesUsecase', function () { it('Should delete files without version (= version 1)', async () => { const fileId = 'file-id'; const v1Files: BucketEntry[] = [fixtures.getBucketEntry({ id: fileId })]; - - stub(bucketEntriesRepository, 'findByIds').resolves(v1Files); - + + stub(bucketEntriesRepository, 'findByIds').resolves(v1Files); + const removeFilesV1Stub = stub(bucketEntriesUsecase, 'removeFilesV1'); const removeFilesV2Spy = jest.spyOn(bucketEntriesUsecase, 'removeFilesV2'); - + await bucketEntriesUsecase.removeFiles([fileId]); - + expect(removeFilesV1Stub.calledOnce).toBeTruthy(); expect(removeFilesV1Stub.firstCall.args).toStrictEqual([v1Files]); expect(removeFilesV2Spy).not.toHaveBeenCalled(); @@ -497,14 +511,14 @@ describe('BucketEntriesUsecase', function () { it('Should delete files with version 1', async () => { const fileId = 'file-id'; const v1Files: BucketEntry[] = [fixtures.getBucketEntry({ id: fileId, version: 1 })]; - - stub(bucketEntriesRepository, 'findByIds').resolves(v1Files); - + + stub(bucketEntriesRepository, 'findByIds').resolves(v1Files); + const removeFilesV1Stub = stub(bucketEntriesUsecase, 'removeFilesV1'); const removeFilesV2Spy = jest.spyOn(bucketEntriesUsecase, 'removeFilesV2'); - + await bucketEntriesUsecase.removeFiles([fileId]); - + expect(removeFilesV1Stub.calledOnce).toBeTruthy(); expect(removeFilesV1Stub.firstCall.args).toStrictEqual([v1Files]); expect(removeFilesV2Spy).not.toHaveBeenCalled(); @@ -515,7 +529,7 @@ describe('BucketEntriesUsecase', function () { const fileId = 'file-id'; const v2Files: BucketEntry[] = [fixtures.getBucketEntry({ id: fileId, version: 2 })]; - stub(bucketEntriesRepository, 'findByIds').resolves(v2Files); + stub(bucketEntriesRepository, 'findByIds').resolves(v2Files); const removeFilesV1Spy = jest.spyOn(bucketEntriesUsecase, 'removeFilesV1'); const removeFilesV2Stub = stub(bucketEntriesUsecase, 'removeFilesV2').rejects(new Error()); @@ -536,7 +550,7 @@ describe('BucketEntriesUsecase', function () { fixtures.getBucketEntry({ version: 2 }) ]; - stub(bucketEntriesRepository, 'findByIds').resolves(v2Files); + stub(bucketEntriesRepository, 'findByIds').resolves(v2Files); const removeFilesV2Stub = stub(bucketEntriesUsecase, 'removeFilesV2').resolves(); const findBucketsStub = stub(bucketsRepository, 'findByIds').rejects(new Error()); @@ -557,10 +571,10 @@ describe('BucketEntriesUsecase', function () { const firstUser = fixtures.getUser(); const firstUserBucket = fixtures.getBucket({ user: firstUser.email, userId: firstUser.uuid }); const firstUserFiles = [ - fixtures.getBucketEntry({ version: 2, bucket: firstUserBucket.id, size: 50 }), + fixtures.getBucketEntry({ version: 2, bucket: firstUserBucket.id, size: 50 }), fixtures.getBucketEntry({ version: 2, bucket: firstUserBucket.id, size: 10 }) ]; - + const secondUser = fixtures.getUser(); const secondUserBucket = fixtures.getBucket({ user: secondUser.email, userId: secondUser.uuid }); const secondUserFile = fixtures.getBucketEntry({ bucket: secondUserBucket.id, version: 2, size: 2 }); @@ -569,7 +583,7 @@ describe('BucketEntriesUsecase', function () { const files = [...firstUserFiles, secondUserFile]; const buckets = [firstUserBucket, secondUserBucket]; - stub(bucketEntriesRepository, 'findByIds').resolves(files); + stub(bucketEntriesRepository, 'findByIds').resolves(files); const removeFilesV2Stub = stub(bucketEntriesUsecase, 'removeFilesV2').resolves(); const findBucketsStub = stub(bucketsRepository, 'findByIds').resolves(buckets); @@ -597,7 +611,7 @@ describe('BucketEntriesUsecase', function () { const totalSizeOfFilesToRemove = filesFromUser.reduce((acumm, f) => acumm + (f.size as number), 0); expect(addTotalSpaceBytesStub.getCalls()[i].args).toStrictEqual([ - users[i].uuid, + users[i].uuid, -totalSizeOfFilesToRemove ]); } diff --git a/yarn.lock b/yarn.lock index 47b479ca..ea59b8cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7326,13 +7326,13 @@ statuses@~1.4.0: semver "^7.3.5" through "^2.3.8" -"storj-mongodb-adapter@github:internxt/mongodb-adapter#10.0.1-beta": +"storj-mongodb-adapter@github:internxt/mongodb-adapter#10.1.0-beta": version "10.0.0-beta" - resolved "https://codeload.github.com/internxt/mongodb-adapter/tar.gz/92a310da95d28d0f0e6368e665475d5062da654c" + resolved "https://codeload.github.com/internxt/mongodb-adapter/tar.gz/52a6305a2fbc1e3c5d3ddfefb905dcde31da25f6" dependencies: mongoose "^7.8.7" storj-lib "github:internxt/core#v8.7.3-beta" - storj-service-storage-models "github:internxt/service-storage-models#11.0.1" + storj-service-storage-models "github:internxt/service-storage-models#11.1.0" "storj-service-error-types@github:internxt/service-error-types": version "1.5.0" @@ -7350,9 +7350,9 @@ statuses@~1.4.0: secp256k1 "^4.0.0" storj-service-error-types "github:internxt/service-error-types" -"storj-service-storage-models@github:internxt/service-storage-models#11.0.1": +"storj-service-storage-models@github:internxt/service-storage-models#11.1.0": version "11.0.1" - resolved "https://codeload.github.com/internxt/service-storage-models/tar.gz/431719289176830d7ecf6c216c555cdb7694a7ac" + resolved "https://codeload.github.com/internxt/service-storage-models/tar.gz/cdfcd9b6fd844f7d454e8f58de2c5763ec3f9fa2" dependencies: bluebird "^3.4.7" coinpayments "^2.1.1"