From 0a03f0c6918ab2c4e986c6fe6c0dc27a78647749 Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Tue, 3 Dec 2024 10:42:38 -0400 Subject: [PATCH] added daily delta on file size change --- src/modules/file/file.module.ts | 2 + src/modules/file/file.repository.ts | 15 +++-- src/modules/file/file.usecase.ts | 16 +++++- src/modules/usage/usage.domain.ts | 19 +++++-- src/modules/usage/usage.repository.ts | 18 ++++++ src/modules/usage/usage.usecase.ts | 81 +++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 13 deletions(-) diff --git a/src/modules/file/file.module.ts b/src/modules/file/file.module.ts index ad13ae9d..b8ce918e 100644 --- a/src/modules/file/file.module.ts +++ b/src/modules/file/file.module.ts @@ -15,6 +15,7 @@ import { SharingModule } from '../sharing/sharing.module'; import { WorkspacesModule } from '../workspaces/workspaces.module'; import { UserModule } from '../user/user.module'; import { NotificationModule } from '../../externals/notifications/notifications.module'; +import { UsageModule } from '../usage/usage.module'; @Module({ imports: [ @@ -28,6 +29,7 @@ import { NotificationModule } from '../../externals/notifications/notifications. CryptoModule, UserModule, NotificationModule, + UsageModule, ], controllers: [FileController], providers: [SequelizeFileRepository, FileUseCases], diff --git a/src/modules/file/file.repository.ts b/src/modules/file/file.repository.ts index 5136d9e7..3c6159b8 100644 --- a/src/modules/file/file.repository.ts +++ b/src/modules/file/file.repository.ts @@ -597,7 +597,13 @@ export class SequelizeFileRepository implements FileRepository { async sumFileSizesSinceDate( userId: FileAttributes['userId'], sinceDate: Date, + untilDate?: Date, ): Promise { + const timeCondition = { + [Op.gte]: sinceDate, + ...(untilDate ? { [Op.lte]: untilDate } : null), + }; + const result = await this.fileModel.findAll({ attributes: [ [ @@ -620,19 +626,16 @@ export class SequelizeFileRepository implements FileRepository { status: { [Op.ne]: 'DELETED', }, - createdAt: { - [Op.gte]: sinceDate, - }, + createdAt: timeCondition, }, { status: 'DELETED', - updatedAt: { - [Op.gte]: sinceDate, - }, + updatedAt: timeCondition, }, ], }, raw: true, + logging: console.log, }); return Number(result[0]['total']) as unknown as number; diff --git a/src/modules/file/file.usecase.ts b/src/modules/file/file.usecase.ts index d8153471..6736a9ed 100644 --- a/src/modules/file/file.usecase.ts +++ b/src/modules/file/file.usecase.ts @@ -36,6 +36,7 @@ import { WorkspaceAttributes } from '../workspaces/attributes/workspace.attribut import { Folder } from '../folder/folder.domain'; import { getPathFileData } from '../../lib/path'; import { isStringEmpty } from '../../lib/validators'; +import { UsageUseCases } from '../usage/usage.usecase'; export type SortParamsFile = Array<[SortableFileAttributes, 'ASC' | 'DESC']>; @@ -49,6 +50,7 @@ export class FileUseCases { private sharingUsecases: SharingService, private network: BridgeService, private cryptoService: CryptoService, + private usageUsecases: UsageUseCases, ) {} getByUuid(uuid: FileAttributes['uuid']): Promise { @@ -540,7 +542,7 @@ export class FileUseCases { ): Promise { const file = await this.fileRepository.findByUuid(fileUuid, user.id); - if (!file) { + if (!file || file?.status != FileStatus.EXISTS) { throw new NotFoundException(`File ${fileUuid} not found`); } @@ -551,7 +553,17 @@ export class FileUseCases { fileId, size, }); - await this.network.deleteFile(user, bucket, oldFileId); + + const newFile = File.build({ ...file, size, fileId }); + + await Promise.all([ + this.network.deleteFile(user, bucket, oldFileId), + this.usageUsecases.addDailyUsageChangeOnFileSizeChange( + user, + file, + newFile, + ), + ]); return { ...file.toJSON(), diff --git a/src/modules/usage/usage.domain.ts b/src/modules/usage/usage.domain.ts index 65f023c9..aef0a7a7 100644 --- a/src/modules/usage/usage.domain.ts +++ b/src/modules/usage/usage.domain.ts @@ -1,9 +1,14 @@ +export enum UsageType { + Daily = 'daily', + Monthly = 'monthly', + Yearly = 'yearly', +} export interface UsageAttributes { id: string; userId: string; delta: number; period: Date; - type: string; + type: UsageType; createdAt: Date; updatedAt: Date; } @@ -13,7 +18,7 @@ export class Usage implements UsageAttributes { userId: string; delta: number; period: Date; - type: string; + type: UsageType; createdAt: Date; updatedAt: Date; @@ -29,7 +34,7 @@ export class Usage implements UsageAttributes { this.id = id; this.userId = userId; this.delta = delta; - this.period = period; + this.period = new Date(period); this.type = type; this.createdAt = createdAt; this.updatedAt = updatedAt; @@ -40,11 +45,15 @@ export class Usage implements UsageAttributes { } isYearly(): boolean { - return this.type === 'yearly'; + return this.type === UsageType.Yearly; } isMonthly(): boolean { - return this.type === 'monthly'; + return this.type === UsageType.Monthly; + } + + isDaily(): boolean { + return this.type === UsageType.Daily; } toJSON(): Partial { diff --git a/src/modules/usage/usage.repository.ts b/src/modules/usage/usage.repository.ts index 01fbe171..1870842c 100644 --- a/src/modules/usage/usage.repository.ts +++ b/src/modules/usage/usage.repository.ts @@ -18,6 +18,12 @@ export class SequelizeUsageRepository { return usages.map((usage) => this.toDomain(usage)); } + public async create(usage: Omit) { + const newUsage = await this.usageModel.create(usage); + + return this.toDomain(newUsage); + } + public async getMostRecentUsage(userUuid: string): Promise { const mostRecentUsage = await this.usageModel.findOne({ where: { userId: userUuid }, @@ -27,6 +33,18 @@ export class SequelizeUsageRepository { return mostRecentUsage ? this.toDomain(mostRecentUsage) : null; } + public async getUsage( + where: Partial, + order?: Array<[keyof Usage, 'ASC' | 'DESC']>, + ): Promise { + const mostRecentUsage = await this.usageModel.findOne({ + where: { ...where }, + order: order, + }); + + return mostRecentUsage ? this.toDomain(mostRecentUsage) : null; + } + public async addFirstDailyUsage(userUuid: string): Promise { const query = ` INSERT INTO public.usages (id, user_id, delta, period, type, created_at, updated_at) diff --git a/src/modules/usage/usage.usecase.ts b/src/modules/usage/usage.usecase.ts index 65043227..b8da4e03 100644 --- a/src/modules/usage/usage.usecase.ts +++ b/src/modules/usage/usage.usecase.ts @@ -1,7 +1,11 @@ import { Injectable } from '@nestjs/common'; import { SequelizeUsageRepository } from './usage.repository'; +import { File } from '../file/file.domain'; import { SequelizeFileRepository } from '../file/file.repository'; import { User } from '../user/user.domain'; +import { Usage, UsageType } from './usage.domain'; +import { v4 } from 'uuid'; +import { Time } from '../../lib/time'; @Injectable() export class UsageUseCases { @@ -38,4 +42,81 @@ export class UsageUseCases { id: user.email, }; } + + async createDailyUsage(userUuid: User['uuid'], period: Date, delta: number) { + const dailyUsage = Usage.build({ + id: v4(), + userId: userUuid, + period, + delta, + type: UsageType.Daily, + createdAt: new Date(), + updatedAt: new Date(), + }); + + const createdDailyUsage = await this.usageRepository.create(dailyUsage); + + return createdDailyUsage; + } + + /* async addDailyUsageChangeOnFileSizeChange( + user: User, + oldFileData: File, + newFileData: File, + ) { + const mostRecentDailyUsage = await this.usageRepository.getUsage( + { + type: UsageType.Daily, + userId: user.uuid, + }, + [['createdAt', 'DESC']], + ); + + let calculateChangesSince: Date = mostRecentDailyUsage?.createdAt; + const now = new Date(); + + if ( + !calculateChangesSince || + new Date(calculateChangesSince).toDateString() !== now.toDateString() + ) { + calculateChangesSince = new Date(now); + calculateChangesSince.setUTCHours(0, 0, 0, 0); + } + + const totalStorageChanged = await this.fileRepository.sumFileSizesSinceDate( + user.id, + calculateChangesSince, + ); + + if (newFileData.createdAt.toDateString() !== now.toDateString()) { + const delta = + Number(newFileData.size) - + Number(oldFileData.size) + + totalStorageChanged; + + return this.createDailyUsage(user.uuid, new Date(), delta); + } + + const delta = totalStorageChanged; + + console.log({ totalStorageChanged, delta }); + + return this.createDailyUsage(user.uuid, new Date(), delta); + } */ + + async addDailyUsageChangeOnFileSizeChange( + user: User, + oldFileData: File, + newFileData: File, + ) { + const isFileCreatedToday = Time.isToday(newFileData.createdAt); + + if (isFileCreatedToday) { + return; + } + + const delta = Number(newFileData.size) - Number(oldFileData.size); + + return this.createDailyUsage(user.uuid, new Date(), delta); + } }