diff --git a/src/apps/main/fordwardToWindows.ts b/src/apps/main/fordwardToWindows.ts index b0cc9f986..2c44441d5 100644 --- a/src/apps/main/fordwardToWindows.ts +++ b/src/apps/main/fordwardToWindows.ts @@ -95,6 +95,24 @@ ipcMainDrive.on('FILE_CREATED', (_, payload) => { }); }); +ipcMainDrive.on('FILE_UPDATED', (_, payload) => { + const { nameWithExtension } = payload; + + broadcastToWindows('sync-info-update', { + action: 'UPDATED', + name: nameWithExtension, + }); +}); + +ipcMainDrive.on('FILE_UPDATING', (_, payload) => { + const { nameWithExtension } = payload; + + broadcastToWindows('sync-info-update', { + action: 'UPDATING', + name: nameWithExtension, + }); +}); + ipcMainDrive.on('FILE_UPLOAD_ERROR', (_, payload) => { const { nameWithExtension } = payload; @@ -130,3 +148,12 @@ ipcMainDrive.on('FILE_DELETION_ERROR', (_, payload: FileErrorInfo) => { name: nameWithExtension, }); }); + +ipcMainDrive.on('FILE_UPDATE_ERROR', (_, payload: FileErrorInfo) => { + const { nameWithExtension } = payload; + + broadcastToWindows('sync-info-update', { + action: 'UPDATE_ERROR', + name: nameWithExtension, + }); +}); diff --git a/src/apps/main/tray/handlers.ts b/src/apps/main/tray/handlers.ts index 40ee32769..6c01902d3 100644 --- a/src/apps/main/tray/handlers.ts +++ b/src/apps/main/tray/handlers.ts @@ -60,6 +60,14 @@ ipcMainDrive.on('FILE_UPLOADED', () => { setTrayStatus('IDLE'); }); +ipcMainDrive.on('FILE_UPDATING', () => { + setTrayStatus('SYNCING'); +}); + +ipcMainDrive.on('FILE_UPDATED', () => { + setTrayStatus('IDLE'); +}); + ipcMainDrive.on('FILE_UPLOAD_ERROR', () => { setTrayStatus('ALERT'); }); @@ -75,3 +83,7 @@ ipcMainDrive.on('FILE_RENAME_ERROR', () => { ipcMainDrive.on('FILE_DELETION_ERROR', () => { setTrayStatus('ALERT'); }); + +ipcMainDrive.on('FILE_UPDATE_ERROR', () => { + setTrayStatus('ALERT'); +}); diff --git a/src/apps/renderer/hooks/useSyncInfoSubscriber.tsx b/src/apps/renderer/hooks/useSyncInfoSubscriber.tsx index d8b2049ee..d2a6163af 100644 --- a/src/apps/renderer/hooks/useSyncInfoSubscriber.tsx +++ b/src/apps/renderer/hooks/useSyncInfoSubscriber.tsx @@ -16,6 +16,7 @@ export function useSyncInfoSubscriber() { const itemIsAnError = [ 'UPLOAD_ERROR', + 'UPDATE_ERROR', 'DOWNLOAD_ERROR', 'RENAME_ERROR', 'DELETE_ERROR', @@ -33,7 +34,7 @@ export function useSyncInfoSubscriber() { function clearItems() { setProcessInfoUpdatedPayload((current) => current.filter((item) => - ['UPLOADING', 'DOWNLOADING', 'RENAMING', 'DELETING'].includes( + ['UPLOADING', 'DOWNLOADING', 'RENAMING', 'DELETING', 'UPDATING'].includes( item.action ) ) @@ -47,6 +48,7 @@ export function useSyncInfoSubscriber() { item.action !== 'UPLOADING' && item.action !== 'DOWNLOADING' && item.action !== 'RENAMING' && + item.action !== 'UPDATING' && item.action !== 'DELETING' ); }); diff --git a/src/apps/shared/IPC/events/drive.ts b/src/apps/shared/IPC/events/drive.ts index 2475f1008..4e200ce40 100644 --- a/src/apps/shared/IPC/events/drive.ts +++ b/src/apps/shared/IPC/events/drive.ts @@ -1,10 +1,3 @@ -type FolderEvents = { - FOLDER_CREATING: (payload: { name: string }) => void; - FOLDER_CREATED: (payload: { name: string }) => void; - FOLDER_RENAMING: (payload: { oldName: string; newName: string }) => void; - FOLDER_RENAMED: (payload: { oldName: string; newName: string }) => void; -}; - export type FileInfo = { name: string; extension: string; @@ -33,6 +26,12 @@ type UploadEvents = { FILE_UPLOAD_ERROR: (payload: FileErrorInfo) => void; }; +type UpdateEvents = { + FILE_UPDATING: (payload: FileInfo) => void; + FILE_UPDATED: (payload: FileInfo) => void; + FILE_UPDATE_ERROR: (payload: FileErrorInfo) => void; +}; + type DownloadEvents = { FILE_DOWNLOADING: (payload: FileProgressInfo) => void; FILE_DOWNLOADED: (payload: FileProgressInfo) => void; @@ -57,6 +56,13 @@ type RenameEvents = { FILE_RENAME_ERROR: (payload: FileErrorInfo) => void; }; +type FolderEvents = { + FOLDER_CREATING: (payload: { name: string }) => void; + FOLDER_CREATED: (payload: { name: string }) => void; + FOLDER_RENAMING: (payload: { oldName: string; newName: string }) => void; + FOLDER_RENAMED: (payload: { oldName: string; newName: string }) => void; +}; + type OverwriteEvents = { FILE_OVERWRITED: (payload: { nameWithExtension: string }) => void; }; @@ -78,6 +84,7 @@ type FileEvents = UploadEvents & RenameEvents & OverwriteEvents & MoveEvents & - CloneEvents; + CloneEvents & + UpdateEvents; export type DriveEvents = FolderEvents & FileEvents; diff --git a/src/apps/shared/IPC/events/sync-engine.ts b/src/apps/shared/IPC/events/sync-engine.ts index bf09401ed..f7d662e0c 100644 --- a/src/apps/shared/IPC/events/sync-engine.ts +++ b/src/apps/shared/IPC/events/sync-engine.ts @@ -115,6 +115,27 @@ export type FilesEvents = { FILE_OVERWRITTEN: (payload: { nameWithExtension: string }) => void; FILE_CLONED: (payload: FileUpdatePayload) => void; + + FILE_UPDATING: (payload: { + name: string; + extension: string; + nameWithExtension: string; + size: number; + }) => void; + + FILE_UPDATED: (payload: { + name: string; + extension: string; + nameWithExtension: string; + size: number; + }) => void; + + FILE_UPDATE_ERROR: (payload: { + name: string; + extension: string; + nameWithExtension: string; + error: string; + }) => void; }; export type SyncEngineInvocableFunctions = { diff --git a/src/apps/shared/types.ts b/src/apps/shared/types.ts index 8c204661b..f3a90c3da 100644 --- a/src/apps/shared/types.ts +++ b/src/apps/shared/types.ts @@ -213,6 +213,7 @@ export type ProcessIssue = ProcessInfoBase & { | 'DOWNLOAD_ERROR' | 'RENAME_ERROR' | 'DELETE_ERROR' + | 'UPDATE_ERROR' | 'METADATA_READ_ERROR'; errorName: ProcessErrorName; @@ -223,11 +224,11 @@ export type ProcessInfoUpdatePayload = | (ProcessInfoBase & ( | { - action: 'UPLOADING' | 'DOWNLOADING' | 'RENAMING' | 'DELETING'; + action: 'UPLOADING' | 'DOWNLOADING' | 'RENAMING' | 'DELETING' | 'UPDATING'; progress: number; } | { - action: 'UPLOADED' | 'DOWNLOADED' | 'RENAMED' | 'DELETED'; + action: 'UPLOADED' | 'DOWNLOADED' | 'RENAMED' | 'DELETED' | 'UPDATED'; } )) | ProcessIssue; diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index 5a33533d1..f0e90af03 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -163,6 +163,12 @@ export class BindingsManager { callback(false); } }, + notifyFileUpdatedCallback: ( + absolutePath: string, + callback: (acknowledge: boolean, id: string) => boolean + ) => { + controllers.updateFile.execute(absolutePath, callback); + }, validateDataCallback: () => { Logger.debug('validateDataCallback'); }, diff --git a/src/apps/sync-engine/callbacks-controllers/buildControllers.ts b/src/apps/sync-engine/callbacks-controllers/buildControllers.ts index 1f749a02d..ac9f20ca1 100644 --- a/src/apps/sync-engine/callbacks-controllers/buildControllers.ts +++ b/src/apps/sync-engine/callbacks-controllers/buildControllers.ts @@ -4,6 +4,7 @@ import { DeleteController } from './controllers/DeleteController'; import { DownloadFileController } from './controllers/DownloadFileController'; import { NotifyPlaceholderHydrationFinished } from './controllers/NotifyPlaceholderHydrationFinished'; import { RenameOrMoveController } from './controllers/RenameOrMoveController'; +import { UpdateFileController } from './controllers/UpdateFileController'; import { OfflineRenameOrMoveController } from './controllers/offline/OfflineRenameOrMoveController'; export function buildControllers(container: DependencyContainer) { @@ -14,6 +15,11 @@ export function buildControllers(container: DependencyContainer) { container.offline.folderCreator ); + const updateFileController = new UpdateFileController( + container.fileCreationOrchestrator, + container.fileUpdater + ); + const deleteController = new DeleteController( container.fileDeleter, container.folderDeleter @@ -44,6 +50,7 @@ export function buildControllers(container: DependencyContainer) { return { addFile: addFileController, renameOrMove: renameOrMoveController, + updateFile: updateFileController, delete: deleteController, downloadFile: downloadFileController, offline: { diff --git a/src/apps/sync-engine/callbacks-controllers/controllers/UpdateFileController.ts b/src/apps/sync-engine/callbacks-controllers/controllers/UpdateFileController.ts new file mode 100644 index 000000000..ff520351e --- /dev/null +++ b/src/apps/sync-engine/callbacks-controllers/controllers/UpdateFileController.ts @@ -0,0 +1,45 @@ +import { FileCreationOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileCreationOrchestrator'; +import { FileUpdater } from '../../../../context/virtual-drive/files/application/FileUpdater'; +import { createFilePlaceholderId } from '../../../../context/virtual-drive/files/domain/PlaceholderId'; +import { CallbackController } from './CallbackController'; +import Logger from 'electron-log'; + + +type UpdateCallback = (acknowledge: boolean, id: string) => void; + +export class UpdateFileController extends CallbackController { + + constructor( + private readonly fileCreationOrchestrator: FileCreationOrchestrator, + private readonly fileUpdater: FileUpdater, + ) { + super(); + } + + private updateFile = async ( + oldContentsId: string, + callback: (acknowledge: boolean, id: string) => void + ) => { + try { + const newContentsId = await this.fileCreationOrchestrator.run(oldContentsId); + await this.fileUpdater.run(oldContentsId, newContentsId); + return callback(true, createFilePlaceholderId(newContentsId)); + } catch (error: unknown) { + Logger.error('Error when updating a file: ', error); + callback(false, ''); + } + }; + + async execute(contentsId: string, callback: UpdateCallback) { + const trimmedId = this.trim(contentsId); + + if (this.isFilePlaceholder(trimmedId)) { + const [_, contentsId] = trimmedId.split(':'); + Logger.debug(`Updating file: ${contentsId}`); + this.updateFile(contentsId, callback); + return; + } + + throw new Error(`Placeholder Id not identified: ${trimmedId}`); + } +} diff --git a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts index 5ba634247..78570f1bb 100644 --- a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts +++ b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts @@ -4,6 +4,7 @@ import { FileDeleter } from '../../../../context/virtual-drive/files/application import { FileFinderByContentsId } from '../../../../context/virtual-drive/files/application/FileFinderByContentsId'; import { FilePathUpdater } from '../../../../context/virtual-drive/files/application/FilePathUpdater'; import { FilePlaceholderCreatorFromContentsId } from '../../../../context/virtual-drive/files/application/FilePlaceholderCreatorFromContentsId'; +import { FileUpdater } from '../../../../context/virtual-drive/files/application/FileUpdater'; import { FilesPlaceholderUpdater } from '../../../../context/virtual-drive/files/application/FilesPlaceholderUpdater'; import { FilesPlaceholderCreator } from '../../../../context/virtual-drive/files/application/FilesPlaceholdersCreator'; import { RepositoryPopulator } from '../../../../context/virtual-drive/files/application/RepositoryPopulator'; @@ -22,4 +23,5 @@ export interface FilesContainer { repositoryPopulator: RepositoryPopulator; filesPlaceholderCreator: FilesPlaceholderCreator; filesPlaceholderUpdater: FilesPlaceholderUpdater; + fileUpdater: FileUpdater; } diff --git a/src/apps/sync-engine/dependency-injection/files/builder.ts b/src/apps/sync-engine/dependency-injection/files/builder.ts index 94969cb85..2120903dc 100644 --- a/src/apps/sync-engine/dependency-injection/files/builder.ts +++ b/src/apps/sync-engine/dependency-injection/files/builder.ts @@ -23,6 +23,9 @@ import { InMemoryFileRepository } from '../../../../context/virtual-drive/files/ import { SDKRemoteFileSystem } from '../../../../context/virtual-drive/files/infrastructure/SDKRemoteFileSystem'; import { NodeWinLocalFileSystem } from '../../../../context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem'; import { LocalFileIdProvider } from '../../../../context/virtual-drive/shared/application/LocalFileIdProvider'; +import { DependencyInjectionHttpClientsProvider } from '../common/clients'; +import { FileUpdater } from '../../../../context/virtual-drive/files/application/FileUpdater'; +import { HttpRemoteFileSystem } from '../../../../context/virtual-drive/files/infrastructure/HttpRemoteFileSystem'; export async function buildFilesContainer( folderContainer: FoldersContainer, @@ -36,8 +39,10 @@ export async function buildFilesContainer( const eventHistory = DependencyInjectionEventRepository.get(); const { virtualDrive } = DependencyInjectionVirtualDrive; const sdk = await DependencyInjectionStorageSdk.get(); + const clients = DependencyInjectionHttpClientsProvider.get(); - const remoteFileSystem = new SDKRemoteFileSystem(sdk, crypt, user.bucket); + const remoteSDKFileSystem = new SDKRemoteFileSystem(sdk, crypt, user.bucket); + const remoteHTTPFileSystem = new HttpRemoteFileSystem(clients.newDrive); const localFileSystem = new NodeWinLocalFileSystem( virtualDrive, sharedContainer.relativePathToAbsoluteConverter @@ -48,13 +53,19 @@ export async function buildFilesContainer( const fileFinderByContentsId = new FileFinderByContentsId(repository); const fileDeleter = new FileDeleter( - remoteFileSystem, + remoteSDKFileSystem, localFileSystem, repository, folderContainer.allParentFoldersStatusIsExists, ipcRendererSyncEngine ); + const fileUpdater = new FileUpdater( + remoteHTTPFileSystem, + repository, + ipcRendererSyncEngine + ); + const sameFileWasMoved = new SameFileWasMoved( repository, localFileSystem, @@ -62,7 +73,7 @@ export async function buildFilesContainer( ); const filePathUpdater = new FilePathUpdater( - remoteFileSystem, + remoteSDKFileSystem, localFileSystem, repository, fileFinderByContentsId, @@ -72,7 +83,7 @@ export async function buildFilesContainer( ); const fileCreator = new FileCreator( - remoteFileSystem, + remoteSDKFileSystem, repository, folderContainer.folderFinder, fileDeleter, @@ -120,6 +131,7 @@ export async function buildFilesContainer( repositoryPopulator: repositoryPopulator, filesPlaceholderCreator, filesPlaceholderUpdater, + fileUpdater, }; return { container, subscribers: [] }; diff --git a/src/apps/sync-engine/dependency-injection/folders/builder.ts b/src/apps/sync-engine/dependency-injection/folders/builder.ts index b40d16044..8110f73b6 100644 --- a/src/apps/sync-engine/dependency-injection/folders/builder.ts +++ b/src/apps/sync-engine/dependency-injection/folders/builder.ts @@ -1,4 +1,4 @@ -import { NodeWinLocalFileSystem } from '../../../../context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem'; +import { NodeWinLocalFolderSystem } from '../../../../context/virtual-drive/folders/infrastructure/NodeWinLocalFolderSystem'; import { AllParentFoldersStatusIsExists } from '../../../../context/virtual-drive/folders/application/AllParentFoldersStatusIsExists'; import { FolderByPartialSearcher } from '../../../../context/virtual-drive/folders/application/FolderByPartialSearcher'; import { FolderCreator } from '../../../../context/virtual-drive/folders/application/FolderCreator'; @@ -17,7 +17,7 @@ import { RetrieveAllFolders } from '../../../../context/virtual-drive/folders/ap import { SynchronizeOfflineModifications } from '../../../../context/virtual-drive/folders/application/SynchronizeOfflineModifications'; import { SynchronizeOfflineModificationsOnFolderCreated } from '../../../../context/virtual-drive/folders/application/SynchronizeOfflineModificationsOnFolderCreated'; import { FolderPlaceholderUpdater } from '../../../../context/virtual-drive/folders/application/UpdatePlaceholderFolder'; -import { HttpRemoteFileSystem } from '../../../../context/virtual-drive/folders/infrastructure/HttpRemoteFileSystem'; +import { HttpRemoteFolderSystem } from '../../../../context/virtual-drive/folders/infrastructure/HttpRemoteFolderSystem'; import { InMemoryFolderRepository } from '../../../../context/virtual-drive/folders/infrastructure/InMemoryFolderRepository'; import { InMemoryOfflineFolderRepository } from '../../../../context/virtual-drive/folders/infrastructure/InMemoryOfflineFolderRepository'; import { ipcRendererSyncEngine } from '../../ipcRendererSyncEngine'; @@ -38,8 +38,8 @@ export async function buildFoldersContainer( const repository = new InMemoryFolderRepository(); - const localFileSystem = new NodeWinLocalFileSystem(virtualDrive); - const remoteFileSystem = new HttpRemoteFileSystem( + const localFileSystem = new NodeWinLocalFolderSystem(virtualDrive); + const remoteFileSystem = new HttpRemoteFolderSystem( clients.drive, clients.newDrive ); diff --git a/src/context/virtual-drive/files/application/FileUpdater.ts b/src/context/virtual-drive/files/application/FileUpdater.ts new file mode 100644 index 000000000..e1ae47564 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileUpdater.ts @@ -0,0 +1,62 @@ +import Logger from 'electron-log'; +import { FileStatuses } from '../domain/FileStatus'; +import { File } from '../domain/File'; +import { FileRepository } from '../domain/FileRepository'; +import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { SyncEngineIpc } from '../../../../apps/sync-engine/ipcRendererSyncEngine'; + +export class FileUpdater { + constructor( + private readonly remote: RemoteFileSystem, + private readonly repository: FileRepository, + private readonly ipc: SyncEngineIpc + ) {} + + async run(oldContentsId: File['contentsId'], newContentsId: File['contentsId']): Promise { + const file = this.repository.searchByPartial({ contentsId: oldContentsId }); + + if (!file) { + return; + } + + if (file.status.is(FileStatuses.TRASHED)) { + Logger.warn(`File ${file.path} is trashed. Will ignore...`); + return; + } + + this.ipc.send('FILE_UPDATING', { + name: file.name, + extension: file.type, + nameWithExtension: file.nameWithExtension, + size: file.size, + }); + + try { + file.update(newContentsId); + + await this.remote.update(oldContentsId, file); + await this.repository.update(file, oldContentsId); + + this.ipc.send('FILE_UPDATED', { + name: file.name, + extension: file.type, + nameWithExtension: file.nameWithExtension, + size: file.size, + }); + } catch (error: unknown) { + Logger.error( + `Error updating the file ${file.nameWithExtension}: `, + error + ); + + const message = error instanceof Error ? error.message : 'Unknown error'; + + this.ipc.send('FILE_UPDATE_ERROR', { + name: file.name, + extension: file.type, + nameWithExtension: file.nameWithExtension, + error: message, + }); + } + } +} diff --git a/src/context/virtual-drive/files/domain/File.ts b/src/context/virtual-drive/files/domain/File.ts index e8f831024..d263b1d7c 100644 --- a/src/context/virtual-drive/files/domain/File.ts +++ b/src/context/virtual-drive/files/domain/File.ts @@ -13,6 +13,7 @@ import { ContentsId } from '../../contents/domain/ContentsId'; import { FileMovedDomainEvent } from './events/FileMovedDomainEvent'; import { FileRenamedDomainEvent } from './events/FileRenamedDomainEvent'; import { FilePlaceholderId, createFilePlaceholderId } from './PlaceholderId'; +import { FileUpdatedDomainEvent } from './events/FileUpdatedDomainEvent'; export type FileAttributes = { contentsId: string; @@ -128,6 +129,20 @@ export class File extends AggregateRoot { }) ); } + + update(newContentsId: string) { + this.updatedAt = new Date(); + this._contentsId = new ContentsId(newContentsId); + + this.record( + new FileUpdatedDomainEvent({ + aggregateId: newContentsId, + size: this._size.value, + type: this.type, + newContentsId, + }) + ); + } moveTo(folder: Folder, trackerId: string): void { if (this.folderId === folder.id) { diff --git a/src/context/virtual-drive/files/domain/FileRepository.ts b/src/context/virtual-drive/files/domain/FileRepository.ts index db12ab343..649f2cd0a 100644 --- a/src/context/virtual-drive/files/domain/FileRepository.ts +++ b/src/context/virtual-drive/files/domain/FileRepository.ts @@ -9,5 +9,5 @@ export interface FileRepository { add(file: File): Promise; - update(file: File): Promise; + update(file: File, oldContentsId?: File['contentsId']): Promise; } diff --git a/src/context/virtual-drive/files/domain/events/FileUpdatedDomainEvent.ts b/src/context/virtual-drive/files/domain/events/FileUpdatedDomainEvent.ts new file mode 100644 index 000000000..447f5fce0 --- /dev/null +++ b/src/context/virtual-drive/files/domain/events/FileUpdatedDomainEvent.ts @@ -0,0 +1,43 @@ +import { DomainEvent } from '../../../../shared/domain/DomainEvent'; + +export type CreatedWebdavFileDomainEventAttributes = { + readonly size: number; + readonly type: string; +}; + +export class FileUpdatedDomainEvent extends DomainEvent { + static readonly EVENT_NAME = 'file.updated'; + + readonly size: number; + readonly type: string; + readonly newContentsId: string; + + constructor({ + aggregateId, + eventId, + size, + type, + newContentsId, + }: { + aggregateId: string; + eventId?: string; + size: number; + type: string; + newContentsId: string; + }) { + super({ + eventName: FileUpdatedDomainEvent.EVENT_NAME, + aggregateId, + eventId, + }); + this.size = size; + this.type = type; + this.newContentsId = newContentsId; + } + + toPrimitives(): CreatedWebdavFileDomainEventAttributes { + const { size, type } = this; + + return { size, type }; + } +} diff --git a/src/context/virtual-drive/files/domain/file-systems/RemoteFileSystem.ts b/src/context/virtual-drive/files/domain/file-systems/RemoteFileSystem.ts index 9573f252c..8be612448 100644 --- a/src/context/virtual-drive/files/domain/file-systems/RemoteFileSystem.ts +++ b/src/context/virtual-drive/files/domain/file-systems/RemoteFileSystem.ts @@ -9,4 +9,6 @@ export interface RemoteFileSystem { move(file: File): Promise; rename(file: File): Promise; + + update(oldContentsId: File['contentsId'], file: File): Promise; } diff --git a/src/context/virtual-drive/files/infrastructure/HttpRemoteFileSystem.ts b/src/context/virtual-drive/files/infrastructure/HttpRemoteFileSystem.ts new file mode 100644 index 000000000..3b0f69e0a --- /dev/null +++ b/src/context/virtual-drive/files/infrastructure/HttpRemoteFileSystem.ts @@ -0,0 +1,43 @@ +import { File, FileAttributes } from '../domain/File'; +import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { Axios } from 'axios'; +import Logger from 'electron-log'; + +export class HttpRemoteFileSystem implements RemoteFileSystem { + constructor( + private readonly driveClient: Axios, + ) {} + + persist(): Promise { + throw new Error('Method not implemented.'); + } + trash(): Promise { + throw new Error('Method not implemented.'); + } + move(): Promise { + throw new Error('Method not implemented.'); + } + rename(): Promise { + throw new Error('Method not implemented.'); + } + + async update(oldContentsId: string, file: File): Promise { + const result = await this.driveClient.put( + `${process.env.NEW_DRIVE_URL}/drive/files/${oldContentsId}`, + { + fileId: file.contentsId, + size: file.size, + } + ); + + if (result.status !== 200) { + Logger.error( + '[FILE SYSTEM] File update failed with status: ', + result.status, + result.statusText + ); + + throw new Error('Error when updating file'); + } + } +} diff --git a/src/context/virtual-drive/files/infrastructure/InMemoryFileRepository.ts b/src/context/virtual-drive/files/infrastructure/InMemoryFileRepository.ts index cc7623820..d3f86352f 100644 --- a/src/context/virtual-drive/files/infrastructure/InMemoryFileRepository.ts +++ b/src/context/virtual-drive/files/infrastructure/InMemoryFileRepository.ts @@ -53,11 +53,15 @@ export class InMemoryFileRepository implements FileRepository { this.files.set(file.contentsId, file.attributes()); } - async update(file: File): Promise { - if (!this.files.has(file.contentsId)) { + async update(file: File, oldContentsId?: File['contentsId']): Promise { + if (!this.files.has(file.contentsId) || (oldContentsId && !this.files.has(oldContentsId))) { throw new Error('File not found'); } + if (oldContentsId) { + this.files.delete(oldContentsId); + } + return this.add(file); } } diff --git a/src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts b/src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts index d0b85607a..f1d07929b 100644 --- a/src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts +++ b/src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts @@ -11,7 +11,7 @@ export class SDKRemoteFileSystem implements RemoteFileSystem { constructor( private readonly sdk: Storage, private readonly crypt: Crypt, - private readonly bucket: string + private readonly bucket: string, ) {} async persist(offline: OfflineFile): Promise { @@ -74,4 +74,8 @@ export class SDKRemoteFileSystem implements RemoteFileSystem { bucketId: this.bucket, }); } + + update(): Promise { + throw new Error('Method not implemented.'); + } } diff --git a/src/context/virtual-drive/folders/application/FolderCreator.ts b/src/context/virtual-drive/folders/application/FolderCreator.ts index 6b09c3eea..2019b7417 100644 --- a/src/context/virtual-drive/folders/application/FolderCreator.ts +++ b/src/context/virtual-drive/folders/application/FolderCreator.ts @@ -3,12 +3,12 @@ import { EventBus } from '../../shared/domain/EventBus'; import { Folder } from '../domain/Folder'; import { FolderRepository } from '../domain/FolderRepository'; import { OfflineFolder } from '../domain/OfflineFolder'; -import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { RemoteFolderSystem } from '../domain/file-systems/RemoteFolderSystem'; export class FolderCreator { constructor( private readonly repository: FolderRepository, - private readonly remote: RemoteFileSystem, + private readonly remote: RemoteFolderSystem, private readonly ipc: SyncEngineIpc, private readonly eventBus: EventBus ) {} diff --git a/src/context/virtual-drive/folders/application/FolderDeleter.ts b/src/context/virtual-drive/folders/application/FolderDeleter.ts index 03348721d..77c79b3c8 100644 --- a/src/context/virtual-drive/folders/application/FolderDeleter.ts +++ b/src/context/virtual-drive/folders/application/FolderDeleter.ts @@ -4,14 +4,14 @@ import { ActionNotPermittedError } from '../domain/errors/ActionNotPermittedErro import { FolderNotFoundError } from '../domain/errors/FolderNotFoundError'; import { AllParentFoldersStatusIsExists } from './AllParentFoldersStatusIsExists'; import { FolderRepository } from '../domain/FolderRepository'; -import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; -import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { RemoteFolderSystem } from '../domain/file-systems/RemoteFolderSystem'; +import { LocalFolderSystem } from '../domain/file-systems/LocalFolderSystem'; export class FolderDeleter { constructor( private readonly repository: FolderRepository, - private readonly remote: RemoteFileSystem, - private readonly local: LocalFileSystem, + private readonly remote: RemoteFolderSystem, + private readonly local: LocalFolderSystem, private readonly allParentFoldersStatusIsExists: AllParentFoldersStatusIsExists ) {} diff --git a/src/context/virtual-drive/folders/application/FolderMover.ts b/src/context/virtual-drive/folders/application/FolderMover.ts index f185e9fd8..957ad77a9 100644 --- a/src/context/virtual-drive/folders/application/FolderMover.ts +++ b/src/context/virtual-drive/folders/application/FolderMover.ts @@ -3,12 +3,12 @@ import { FolderPath } from '../domain/FolderPath'; import { Folder } from '../domain/Folder'; import { FolderFinder } from './FolderFinder'; import { FolderRepository } from '../domain/FolderRepository'; -import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { RemoteFolderSystem } from '../domain/file-systems/RemoteFolderSystem'; export class FolderMover { constructor( private readonly repository: FolderRepository, - private readonly remote: RemoteFileSystem, + private readonly remote: RemoteFolderSystem, private readonly folderFinder: FolderFinder ) {} diff --git a/src/context/virtual-drive/folders/application/FolderRenamer.ts b/src/context/virtual-drive/folders/application/FolderRenamer.ts index 8304e025c..bed9c2523 100644 --- a/src/context/virtual-drive/folders/application/FolderRenamer.ts +++ b/src/context/virtual-drive/folders/application/FolderRenamer.ts @@ -1,13 +1,13 @@ import { FolderPath } from '../domain/FolderPath'; import { Folder } from '../domain/Folder'; import { FolderRepository } from '../domain/FolderRepository'; -import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { RemoteFolderSystem } from '../domain/file-systems/RemoteFolderSystem'; import { SyncEngineIpc } from '../../../../apps/sync-engine/ipcRendererSyncEngine'; export class FolderRenamer { constructor( private readonly repository: FolderRepository, - private readonly remote: RemoteFileSystem, + private readonly remote: RemoteFolderSystem, private readonly ipc: SyncEngineIpc ) {} diff --git a/src/context/virtual-drive/folders/application/FoldersPlaceholderCreator.ts b/src/context/virtual-drive/folders/application/FoldersPlaceholderCreator.ts index 62bbf1fb5..855b5a9f2 100644 --- a/src/context/virtual-drive/folders/application/FoldersPlaceholderCreator.ts +++ b/src/context/virtual-drive/folders/application/FoldersPlaceholderCreator.ts @@ -1,8 +1,8 @@ import { Folder } from '../domain/Folder'; -import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { LocalFolderSystem } from '../domain/file-systems/LocalFolderSystem'; export class FoldersPlaceholderCreator { - constructor(private readonly local: LocalFileSystem) {} + constructor(private readonly local: LocalFolderSystem) {} async run(folders: Array): Promise { const createPromises = folders.map((folder) => diff --git a/src/context/virtual-drive/folders/application/UpdatePlaceholderFolder.ts b/src/context/virtual-drive/folders/application/UpdatePlaceholderFolder.ts index 1a6a22618..554323248 100644 --- a/src/context/virtual-drive/folders/application/UpdatePlaceholderFolder.ts +++ b/src/context/virtual-drive/folders/application/UpdatePlaceholderFolder.ts @@ -5,12 +5,12 @@ import Logger from 'electron-log'; import path from 'path'; import { FolderStatuses } from '../domain/FolderStatus'; import { FolderRepository } from '../domain/FolderRepository'; -import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { LocalFolderSystem } from '../domain/file-systems/LocalFolderSystem'; export class FolderPlaceholderUpdater { constructor( private readonly repository: FolderRepository, - private readonly local: LocalFileSystem, + private readonly local: LocalFolderSystem, private readonly relativePathToAbsoluteConverter: RelativePathToAbsoluteConverter ) {} diff --git a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/folders/domain/file-systems/LocalFolderSystem.ts similarity index 71% rename from src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts rename to src/context/virtual-drive/folders/domain/file-systems/LocalFolderSystem.ts index b16434a46..a7bf37366 100644 --- a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/folders/domain/file-systems/LocalFolderSystem.ts @@ -1,5 +1,5 @@ import { Folder } from '../Folder'; -export interface LocalFileSystem { +export interface LocalFolderSystem { createPlaceHolder(folder: Folder): Promise; } diff --git a/src/context/virtual-drive/folders/domain/file-systems/RemoteFileSystem.ts b/src/context/virtual-drive/folders/domain/file-systems/RemoteFolderSystem.ts similarity index 88% rename from src/context/virtual-drive/folders/domain/file-systems/RemoteFileSystem.ts rename to src/context/virtual-drive/folders/domain/file-systems/RemoteFolderSystem.ts index 109e1c62f..4e9c4cbb3 100644 --- a/src/context/virtual-drive/folders/domain/file-systems/RemoteFileSystem.ts +++ b/src/context/virtual-drive/folders/domain/file-systems/RemoteFolderSystem.ts @@ -1,7 +1,7 @@ import { Folder, FolderAttributes } from '../Folder'; import { OfflineFolder } from '../OfflineFolder'; -export interface RemoteFileSystem { +export interface RemoteFolderSystem { persist(offline: OfflineFolder): Promise; trash(id: Folder['id']): Promise; diff --git a/src/context/virtual-drive/folders/infrastructure/HttpRemoteFileSystem.ts b/src/context/virtual-drive/folders/infrastructure/HttpRemoteFolderSystem.ts similarity index 95% rename from src/context/virtual-drive/folders/infrastructure/HttpRemoteFileSystem.ts rename to src/context/virtual-drive/folders/infrastructure/HttpRemoteFolderSystem.ts index fa86fe46c..609e3581f 100644 --- a/src/context/virtual-drive/folders/infrastructure/HttpRemoteFileSystem.ts +++ b/src/context/virtual-drive/folders/infrastructure/HttpRemoteFolderSystem.ts @@ -4,12 +4,12 @@ import * as uuid from 'uuid'; import { Folder, FolderAttributes } from '../domain/Folder'; import { FolderStatuses } from '../domain/FolderStatus'; import { UpdateFolderNameDTO } from './dtos/UpdateFolderNameDTO'; -import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { RemoteFolderSystem } from '../domain/file-systems/RemoteFolderSystem'; import { OfflineFolder } from '../domain/OfflineFolder'; import { ServerFolder } from '../../../shared/domain/ServerFolder'; import { CreateFolderDTO } from './dtos/CreateFolderDTO'; -export class HttpRemoteFileSystem implements RemoteFileSystem { +export class HttpRemoteFolderSystem implements RemoteFolderSystem { public folders: Record = {}; constructor( diff --git a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFolderSystem.ts similarity index 79% rename from src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts rename to src/context/virtual-drive/folders/infrastructure/NodeWinLocalFolderSystem.ts index 1c1a2d5b2..0856ce282 100644 --- a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFolderSystem.ts @@ -1,9 +1,9 @@ import { VirtualDrive } from 'virtual-drive/dist'; import { Folder } from '../domain/Folder'; import { FolderStatuses } from '../domain/FolderStatus'; -import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { LocalFolderSystem } from '../domain/file-systems/LocalFolderSystem'; -export class NodeWinLocalFileSystem implements LocalFileSystem { +export class NodeWinLocalFolderSystem implements LocalFolderSystem { constructor(private readonly virtualDrive: VirtualDrive) {} async createPlaceHolder(folder: Folder): Promise { diff --git a/tests/context/virtual-drive/files/__mocks__/RemoteFileSystemMock.ts b/tests/context/virtual-drive/files/__mocks__/RemoteFileSystemMock.ts index 76473803f..b7bd2ac81 100644 --- a/tests/context/virtual-drive/files/__mocks__/RemoteFileSystemMock.ts +++ b/tests/context/virtual-drive/files/__mocks__/RemoteFileSystemMock.ts @@ -10,6 +10,7 @@ export class RemoteFileSystemMock implements RemoteFileSystem { public readonly trashMock = jest.fn(); public readonly moveMock = jest.fn(); public readonly renameMock = jest.fn(); + public readonly updateMock = jest.fn(); persist(offline: OfflineFile): Promise { return this.persistMock(offline); @@ -26,4 +27,8 @@ export class RemoteFileSystemMock implements RemoteFileSystem { rename(file: File): Promise { return this.renameMock(file); } + + update(oldContentsId: string, file: File): Promise { + return this.updateMock(oldContentsId, file); + } } diff --git a/tests/context/virtual-drive/folders/__mocks__/FolderLocalFileSystemMock.ts b/tests/context/virtual-drive/folders/__mocks__/FolderLocalFileSystemMock.ts index eccabee32..88a2e6007 100644 --- a/tests/context/virtual-drive/folders/__mocks__/FolderLocalFileSystemMock.ts +++ b/tests/context/virtual-drive/folders/__mocks__/FolderLocalFileSystemMock.ts @@ -1,7 +1,7 @@ import { Folder } from '../../../../../src/context/virtual-drive/folders/domain/Folder'; -import { LocalFileSystem } from '../../../../../src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem'; +import { LocalFolderSystem } from '../../../../../src/context/virtual-drive/folders/domain/file-systems/LocalFolderSystem'; -export class FolderLocalFileSystemMock implements LocalFileSystem { +export class FolderLocalFileSystemMock implements LocalFolderSystem { public readonly createPlaceHolderMock = jest.fn(); createPlaceHolder(folder: Folder): Promise { diff --git a/tests/context/virtual-drive/folders/__mocks__/FolderRemoteFileSystemMock.ts b/tests/context/virtual-drive/folders/__mocks__/FolderRemoteFileSystemMock.ts index 4a43211c3..25d8cb751 100644 --- a/tests/context/virtual-drive/folders/__mocks__/FolderRemoteFileSystemMock.ts +++ b/tests/context/virtual-drive/folders/__mocks__/FolderRemoteFileSystemMock.ts @@ -3,9 +3,9 @@ import { FolderAttributes, } from '../../../../../src/context/virtual-drive/folders/domain/Folder'; import { OfflineFolder } from '../../../../../src/context/virtual-drive/folders/domain/OfflineFolder'; -import { RemoteFileSystem } from '../../../../../src/context/virtual-drive/folders/domain/file-systems/RemoteFileSystem'; +import { RemoteFolderSystem } from '../../../../../src/context/virtual-drive/folders/domain/file-systems/RemoteFolderSystem'; -export class FolderRemoteFileSystemMock implements RemoteFileSystem { +export class FolderRemoteFileSystemMock implements RemoteFolderSystem { public readonly persistMock = jest.fn(); public readonly trashMock = jest.fn(); public readonly moveMock = jest.fn();