From 0734a2c15e2e3ac6a15b583273b095c30eba6ffc Mon Sep 17 00:00:00 2001 From: joan vicens Date: Wed, 10 Jan 2024 12:51:07 +0100 Subject: [PATCH 1/8] fix: delete unused variable --- .../virtual-drive/shared/application/PlatformPathConverter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/virtual-drive/shared/application/PlatformPathConverter.ts b/src/context/virtual-drive/shared/application/PlatformPathConverter.ts index fb1133e1f..1398bf62e 100644 --- a/src/context/virtual-drive/shared/application/PlatformPathConverter.ts +++ b/src/context/virtual-drive/shared/application/PlatformPathConverter.ts @@ -21,7 +21,7 @@ export class PlatformPathConverter { static getFatherPathPosix(posixPath: string): string { const pathArray = posixPath.split('/'); - const folderToCreate = pathArray.pop(); + pathArray.pop(); const parentPath = pathArray.join('/'); return this.winToPosix(parentPath); } From b0cee45af963af7df77ba1012e6ef95dfd8e9006 Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Wed, 10 Jan 2024 16:00:00 +0100 Subject: [PATCH 2/8] [PB-1352]: fixed address typo --- src/apps/renderer/localize/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/renderer/localize/locales/en.json b/src/apps/renderer/localize/locales/en.json index 95b1a0527..a84d3f135 100644 --- a/src/apps/renderer/localize/locales/en.json +++ b/src/apps/renderer/localize/locales/en.json @@ -1,7 +1,7 @@ { "login": { "email": { - "section": "Email adress" + "section": "Email address" }, "password": { "section": "Password", From 69074d8c8e22f9907a3a47b1a068ecfa2ea6d3de Mon Sep 17 00:00:00 2001 From: larry-internxt Date: Wed, 10 Jan 2024 16:01:22 +0100 Subject: [PATCH 3/8] [PB-1353]: fixed Log in typo --- src/apps/renderer/localize/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/renderer/localize/locales/en.json b/src/apps/renderer/localize/locales/en.json index a84d3f135..466044150 100644 --- a/src/apps/renderer/localize/locales/en.json +++ b/src/apps/renderer/localize/locales/en.json @@ -11,7 +11,7 @@ "show": "Show" }, "action": { - "login": "Login", + "login": "Log in", "is-logging-in": "Logging you in..." }, "create-account": "Create account", From 9cbfc6467e134b7026ee3f93310b7a0a5a17291e Mon Sep 17 00:00:00 2001 From: Joan Vicens Date: Thu, 11 Jan 2024 14:12:22 +0000 Subject: [PATCH 4/8] [PB-1489] Fix: ignore nodes with same name (#438) * fix: ignore duplicated nodes * feat: comunicate the traverser error to user * test: update traverser test * chore: allow long error messages --- .../background-processes/process-issues.ts | 10 ++ src/apps/main/fordwardToWindows.ts | 8 ++ src/apps/renderer/localize/locales/en.json | 8 +- src/apps/renderer/localize/locales/es.json | 6 +- src/apps/renderer/localize/locales/fr.json | 6 +- src/apps/renderer/messages/process-error.ts | 2 + .../renderer/pages/ProcessIssues/List.tsx | 2 +- src/apps/renderer/pages/Widget/Item.tsx | 9 +- src/apps/shared/IPC/events/sync-engine.ts | 4 + src/apps/shared/types.ts | 8 +- .../dependency-injection/common/traverser.ts | 7 +- .../dependency-injection/items/builder.ts | 2 + .../items/application/Traverser.ts | 99 ++++++++++++------- .../virtual-drive/items/domain/FolderNode.ts | 3 +- .../items/application/Traverser.test.ts | 11 +++ 15 files changed, 132 insertions(+), 53 deletions(-) diff --git a/src/apps/main/background-processes/process-issues.ts b/src/apps/main/background-processes/process-issues.ts index f62c58376..fbbcd4570 100644 --- a/src/apps/main/background-processes/process-issues.ts +++ b/src/apps/main/background-processes/process-issues.ts @@ -72,6 +72,16 @@ ipcMain.on('SYNC_INFO_UPDATE', (_, payload: ProcessInfoUpdatePayload) => { } }); +ipcMain.on('SYNC_PROBLEM', (_, payload) => { + addProcessIssue({ + action: 'GENERATE_TREE', + process: 'SYNC', + errorName: 'DUPLICATED_NODE', + kind: 'LOCAL', + name: payload.additionalData.name, + }); +}); + ipcMain.on('BACKUP_ISSUE', (_, issue: ProcessIssue) => { addProcessIssue(issue); }); diff --git a/src/apps/main/fordwardToWindows.ts b/src/apps/main/fordwardToWindows.ts index b0cc9f986..a592cef9a 100644 --- a/src/apps/main/fordwardToWindows.ts +++ b/src/apps/main/fordwardToWindows.ts @@ -1,5 +1,6 @@ import { broadcastToWindows } from './windows'; import { ipcMainDrive } from './ipcs/mainDrive'; +import { ipcMainSyncEngine } from './ipcs/ipcMainSyncEngine'; import { FileErrorInfo } from '../shared/IPC/events/drive'; ipcMainDrive.on('FILE_DELETED', (_, payload) => { @@ -130,3 +131,10 @@ ipcMainDrive.on('FILE_DELETION_ERROR', (_, payload: FileErrorInfo) => { name: nameWithExtension, }); }); + +ipcMainSyncEngine.on('SYNC_PROBLEM', (_, payload) => { + broadcastToWindows('sync-info-update', { + action: 'SYNC_PROBLEM', + name: payload.additionalData.name, + }); +}); diff --git a/src/apps/renderer/localize/locales/en.json b/src/apps/renderer/localize/locales/en.json index 466044150..d5d0c02a4 100644 --- a/src/apps/renderer/localize/locales/en.json +++ b/src/apps/renderer/localize/locales/en.json @@ -321,7 +321,8 @@ "no-permission": "Insufficient permissions", "file-does-not-exist": "File doesn't exist", "file-too-big": "Max upload size is 20GB. Please try smaller files.", - "file-non-extension": "Files without extensions are not supported." + "file-non-extension": "Files without extensions are not supported.", + "duplicated-node": "It appears that there are duplicate file or folder names in this directory. Please rename one of them to ensure synchronization" }, "error-messages": { "no-internet": "Looks like you are not connected to the internet, we'll try again later", @@ -334,9 +335,10 @@ "unknown": "An unknown error ocurred while trying to sync your files", "empty-file": "We don't support files with a size of 0 bytes because of our processes of sharding and encryption", "bad-response": "We got a bad response from our servers while processing this file. Please, try starting the sync process again.", - "file-does-not-exist": "This file was present when we compared your local folder with your Internxt drive but dissapeared when we tried to access it. If you deleted this file, don't worry, this error should dissapear the next time the sync process starts.", + "file-does-not-exist": "This file was present when we compared your local folder with your Internxt drive but disappeared when we tried to access it. If you deleted this file, don't worry, this error should dissapear the next time the sync process starts.", "file-too-big": "Max upload size is 20GB. Please try smaller files.", - "file-non-extension": "Files without extensions are not supported. Not synchronized." + "file-non-extension": "Files without extensions are not supported. Not synchronized.", + "duplicated-node": "There are two elements (file or folder) with the same name on a folder. Rename one of them to sync them both" }, "report-modal": { "actions": { diff --git a/src/apps/renderer/localize/locales/es.json b/src/apps/renderer/localize/locales/es.json index 99c0e4fde..e66dd663f 100644 --- a/src/apps/renderer/localize/locales/es.json +++ b/src/apps/renderer/localize/locales/es.json @@ -320,7 +320,8 @@ "no-permission": "Permisos insuficientes", "file-does-not-exist": "El archivo no existe", "file-too-big": "El tamaño máximo de carga es de 20GB. Por favor, intenta con archivos más pequeños.", - "file-non-extension": "Archivos sin extensión no son soportados" + "file-non-extension": "Archivos sin extensión no son soportados", + "duplicated-node": "Hay un conflicto con el elemento" }, "error-messages": { "no-internet": "No estás conectado a Internet, lo intentaremos más tarde", @@ -335,7 +336,8 @@ "bad-response": "Error de servidor al procesar este archivo. Por favor, intente iniciar de nuevo el proceso de sincronización", "file-does-not-exist": "Este archivo estaba presente cuando comparamos su carpeta local con su unidad Internxt, pero desapareció cuando intentamos acceder a él. Si has eliminado este archivo, no te preocupes, este error debería desaparecer la próxima vez que se inicie el proceso de sincronización", "file-too-big": "El tamaño máximo de carga es de 20GB. Por favor, intenta con archivos más pequeños.", - "file-non-extension": "Los archivos sin extensiones no son soportados. No sincronizado" + "file-non-extension": "Los archivos sin extensiones no son soportados. No sincronizado", + "duplicated-node": "Hay dos elementos (archivo o carpeta) con el mismo nombre en una carpeta. Cambia el nombre de uno de ellos para sincronizar ambos." }, "report-modal": { "actions": { diff --git a/src/apps/renderer/localize/locales/fr.json b/src/apps/renderer/localize/locales/fr.json index d34615e7b..b57c8821c 100644 --- a/src/apps/renderer/localize/locales/fr.json +++ b/src/apps/renderer/localize/locales/fr.json @@ -314,7 +314,8 @@ "no-internet-connection": "Pas de connexion internet", "no-permission": "Permissions insuffisantes", "file-does-not-exist": "Le fichier n'existe pas", - "file-too-big": "La taille maximale de téléchargement est de 20 GB. Veuillez essayer des fichiers plus petits." + "file-too-big": "La taille maximale de téléchargement est de 20 GB. Veuillez essayer des fichiers plus petits.", + "duplicated-node": "Il y a un conflit avec l'élément." }, "error-messages": { "no-internet": "Il semble que vous ne soyez pas connecté à l'internet, nous réessayerons plus tard", @@ -329,7 +330,8 @@ "bad-response": "Nous avons reçu une mauvaise réponse de nos serveurs lors du traitement de ce fichier. Veuillez essayer de relancer le processus de synchronisation.", "file-does-not-exist": "Ce fichier était présent lorsque nous avons comparé votre dossier local avec votre disque interne, mais il a disparu lorsque nous avons essayé d'y accéder. Si vous avez supprimé ce fichier, ne vous inquiétez pas, cette erreur devrait disparaître au prochain démarrage du processus de synchronisation.", "file-too-big": "La taille maximale de téléchargement est de 20 GB. Veuillez essayer des fichiers plus petits.", - "file-non-extension": "Les archives sans extensions ne sont pas supportées. Non synchronisées" + "file-non-extension": "Les archives sans extensions ne sont pas supportées. Non synchronisées", + "duplicated-node": "Il y a deux éléments (fichier ou dossier) avec le même nom dans un dossier. Renommez l'un d'eux pour les synchroniser tous les deux." }, "report-modal": { "actions": { diff --git a/src/apps/renderer/messages/process-error.ts b/src/apps/renderer/messages/process-error.ts index 34df86b4a..d381f4d8c 100644 --- a/src/apps/renderer/messages/process-error.ts +++ b/src/apps/renderer/messages/process-error.ts @@ -12,6 +12,7 @@ export const shortMessages: ProcessErrorMessages = { UNKNOWN: 'issues.short-error-messages.unknown', FILE_TOO_BIG: 'issues.short-error-messages.file-too-big', FILE_NON_EXTENSION: 'issues.short-error-messages.file-non-extension', + DUPLICATED_NODE: 'issues.short-error-messages.duplicated-node', }; export const longMessages: ProcessErrorMessages = { @@ -25,4 +26,5 @@ export const longMessages: ProcessErrorMessages = { UNKNOWN: 'issues.error-messages.unknown', FILE_TOO_BIG: 'issues.error-messages.file-too-big', FILE_NON_EXTENSION: 'issues.error-messages.file-non-extension', + DUPLICATED_NODE: 'issues.error-messages.duplicated-node', }; diff --git a/src/apps/renderer/pages/ProcessIssues/List.tsx b/src/apps/renderer/pages/ProcessIssues/List.tsx index a6887aade..eee9a0e82 100644 --- a/src/apps/renderer/pages/ProcessIssues/List.tsx +++ b/src/apps/renderer/pages/ProcessIssues/List.tsx @@ -202,7 +202,7 @@ function Item({

{translate(shortMessages[errorName])} diff --git a/src/apps/renderer/pages/Widget/Item.tsx b/src/apps/renderer/pages/Widget/Item.tsx index 5bd6f9ab8..f1397d12a 100644 --- a/src/apps/renderer/pages/Widget/Item.tsx +++ b/src/apps/renderer/pages/Widget/Item.tsx @@ -68,7 +68,8 @@ export function Item({ action === 'DOWNLOAD_ERROR' || action === 'UPLOAD_ERROR' || action === 'RENAME_ERROR' || - action === 'METADATA_READ_ERROR') + action === 'METADATA_READ_ERROR' || + action === 'GENERATE_TREE') ? 'text-red' : undefined }`} @@ -78,7 +79,8 @@ export function Item({ action === 'DOWNLOAD_ERROR' || action === 'UPLOAD_ERROR' || action === 'RENAME_ERROR' || - action === 'METADATA_READ_ERROR') + action === 'METADATA_READ_ERROR' || + action === 'GENERATE_TREE') ? description : undefined } @@ -123,7 +125,8 @@ export function Item({ action === 'DOWNLOAD_ERROR' || action === 'UPLOAD_ERROR' || action === 'RENAME_ERROR' || - action === 'METADATA_READ_ERROR') && ( + action === 'METADATA_READ_ERROR' || + action === 'GENERATE_TREE') && ( )}

diff --git a/src/apps/shared/IPC/events/sync-engine.ts b/src/apps/shared/IPC/events/sync-engine.ts index bf09401ed..2b9494796 100644 --- a/src/apps/shared/IPC/events/sync-engine.ts +++ b/src/apps/shared/IPC/events/sync-engine.ts @@ -128,6 +128,10 @@ export type SyncEngineInvocableFunctions = { // TODO: change how errors are reported to the ui export type ProcessInfoUpdate = { SYNC_INFO_UPDATE: (payload: ProcessInfoUpdatePayload) => void; + SYNC_PROBLEM: (payload: { + key: string; + additionalData: Record; + }) => void; }; export type FromProcess = FilesEvents & diff --git a/src/apps/shared/types.ts b/src/apps/shared/types.ts index 8c204661b..d77562c62 100644 --- a/src/apps/shared/types.ts +++ b/src/apps/shared/types.ts @@ -149,7 +149,10 @@ export type ProcessErrorName = | 'FILE_NON_EXTENSION' // Unknown error - | 'UNKNOWN'; + | 'UNKNOWN' + + // Duplicated node path + | 'DUPLICATED_NODE'; export class ProcessError extends Error { details: ErrorDetails; @@ -213,7 +216,8 @@ export type ProcessIssue = ProcessInfoBase & { | 'DOWNLOAD_ERROR' | 'RENAME_ERROR' | 'DELETE_ERROR' - | 'METADATA_READ_ERROR'; + | 'METADATA_READ_ERROR' + | 'GENERATE_TREE'; errorName: ProcessErrorName; process: 'SYNC' | 'BACKUPS'; diff --git a/src/apps/sync-engine/dependency-injection/common/traverser.ts b/src/apps/sync-engine/dependency-injection/common/traverser.ts index b25de6491..be17a4e0c 100644 --- a/src/apps/sync-engine/dependency-injection/common/traverser.ts +++ b/src/apps/sync-engine/dependency-injection/common/traverser.ts @@ -1,6 +1,7 @@ import crypt from '../../../../context/shared/infrastructure/crypt'; import { Traverser } from '../../../../context/virtual-drive/items/application/Traverser'; import { DependencyInjectionUserProvider } from './user'; +import { ipcRendererSyncEngine } from '../../ipcRendererSyncEngine'; export class DependencyInjectionTraverserProvider { private static traverser: Traverser; @@ -12,7 +13,11 @@ export class DependencyInjectionTraverserProvider { const user = DependencyInjectionUserProvider.get(); - const traverser = Traverser.existingItems(crypt, user.root_folder_id); + const traverser = Traverser.existingItems( + crypt, + ipcRendererSyncEngine, + user.root_folder_id + ); DependencyInjectionTraverserProvider.traverser = traverser; diff --git a/src/apps/sync-engine/dependency-injection/items/builder.ts b/src/apps/sync-engine/dependency-injection/items/builder.ts index c6f66a759..20a7c7ffd 100644 --- a/src/apps/sync-engine/dependency-injection/items/builder.ts +++ b/src/apps/sync-engine/dependency-injection/items/builder.ts @@ -19,10 +19,12 @@ export function buildItemsContainer(): ItemsContainer { const existingItemsTraverser = Traverser.existingItems( nameDecryptor, + ipcRendererSyncEngine, user.root_folder_id ); const allStatusesTraverser = Traverser.allItems( nameDecryptor, + ipcRendererSyncEngine, user.root_folder_id ); const treeBuilder = new TreeBuilder( diff --git a/src/context/virtual-drive/items/application/Traverser.ts b/src/context/virtual-drive/items/application/Traverser.ts index 53c964a71..f6a312553 100644 --- a/src/context/virtual-drive/items/application/Traverser.ts +++ b/src/context/virtual-drive/items/application/Traverser.ts @@ -1,14 +1,6 @@ +import * as Sentry from '@sentry/electron/renderer'; import Logger from 'electron-log'; -import { - FolderStatus, - FolderStatuses, -} from '../../folders/domain/FolderStatus'; -import { Folder } from '../../folders/domain/Folder'; -import { EitherTransformer } from '../../shared/application/EitherTransformer'; -import { createFileFromServerFile } from '../../files/application/FileCreatorFromServerFile'; -import { createFolderFromServerFolder } from '../../folders/application/FolderCreatorFromServerFolder'; -import { NameDecrypt } from '../domain/NameDecrypt'; -import { Tree } from '../domain/Tree'; +import { SyncEngineIpc } from '../../../../apps/sync-engine/ipcRendererSyncEngine'; import { ServerFile, ServerFileStatus, @@ -17,6 +9,16 @@ import { ServerFolder, ServerFolderStatus, } from '../../../shared/domain/ServerFolder'; +import { createFileFromServerFile } from '../../files/application/FileCreatorFromServerFile'; +import { createFolderFromServerFolder } from '../../folders/application/FolderCreatorFromServerFolder'; +import { Folder } from '../../folders/domain/Folder'; +import { + FolderStatus, + FolderStatuses, +} from '../../folders/domain/FolderStatus'; +import { EitherTransformer } from '../../shared/application/EitherTransformer'; +import { NameDecrypt } from '../domain/NameDecrypt'; +import { Tree } from '../domain/Tree'; type Items = { files: Array; @@ -26,22 +28,32 @@ type Items = { export class Traverser { constructor( private readonly decrypt: NameDecrypt, + private readonly ipc: SyncEngineIpc, private readonly baseFolderId: number, private readonly fileStatusesToFilter: Array, private readonly folderStatusesToFilter: Array ) {} - static existingItems(decrypt: NameDecrypt, baseFolderId: number): Traverser { + static existingItems( + decrypt: NameDecrypt, + ipc: SyncEngineIpc, + baseFolderId: number + ): Traverser { return new Traverser( decrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS], [ServerFolderStatus.EXISTS] ); } - static allItems(decrypt: NameDecrypt, baseFolderId: number): Traverser { - return new Traverser(decrypt, baseFolderId, [], []); + static allItems( + decrypt: NameDecrypt, + ipc: SyncEngineIpc, + baseFolderId: number + ): Traverser { + return new Traverser(decrypt, ipc, baseFolderId, [], []); } private createRootFolder(): Folder { @@ -69,17 +81,17 @@ export class Traverser { return folder.parentId === currentFolder.id; }); - filesInThisFolder.forEach((file) => { - if (!this.fileStatusesToFilter.includes(file.status)) { + filesInThisFolder.forEach((serverFile) => { + if (!this.fileStatusesToFilter.includes(serverFile.status)) { return; } const decryptedName = this.decrypt.decryptName( - file.name, - file.folderId.toString(), - file.encrypt_version + serverFile.name, + serverFile.folderId.toString(), + serverFile.encrypt_version ); - const extensionToAdd = file.type ? `.${file.type}` : ''; + const extensionToAdd = serverFile.type ? `.${serverFile.type}` : ''; const relativeFilePath = `${currentFolder.path}/${decryptedName}${extensionToAdd}`.replaceAll( @@ -87,17 +99,22 @@ export class Traverser { '/' ); - EitherTransformer.handleWithEither(() => - createFileFromServerFile(file, relativeFilePath) - ).fold( - (error) => { - Logger.warn( - `[Traverser] File with path ${relativeFilePath} could not be created: `, - error - ); + EitherTransformer.handleWithEither(() => { + const file = createFileFromServerFile(serverFile, relativeFilePath); + tree.addFile(currentFolder, file); + }).fold( + (error): void => { + Logger.warn('[Traverser] Error adding file:', error); + Sentry.captureException(error); + this.ipc.send('SYNC_PROBLEM', { + key: 'node-duplicated', + additionalData: { + name: serverFile.plainName, + }, + }); }, - (file) => { - tree.addFile(currentFolder, file); + () => { + // no-op } ); }); @@ -118,18 +135,24 @@ export class Traverser { return; } - EitherTransformer.handleWithEither(() => - createFolderFromServerFolder(serverFolder, name) - ).fold( + EitherTransformer.handleWithEither(() => { + const folder = createFolderFromServerFolder(serverFolder, name); + + tree.addFolder(currentFolder, folder); + + return folder; + }).fold( (error) => { - Logger.warn( - `[Traverser] Folder with path ${name} could not be created: `, - error - ); + Logger.warn(`[Traverser] Error adding folder: ${error} `); + Sentry.captureException(error); + this.ipc.send('SYNC_PROBLEM', { + key: 'node-duplicated', + additionalData: { + name, + }, + }); }, (folder) => { - tree.addFolder(currentFolder, folder); - if (folder.hasStatus(FolderStatuses.EXISTS)) { // The folders and the files inside trashed or deleted folders // will have the status "EXISTS", to avoid filtering witch folders and files diff --git a/src/context/virtual-drive/items/domain/FolderNode.ts b/src/context/virtual-drive/items/domain/FolderNode.ts index f14c98718..3a2565cfd 100644 --- a/src/context/virtual-drive/items/domain/FolderNode.ts +++ b/src/context/virtual-drive/items/domain/FolderNode.ts @@ -1,6 +1,7 @@ import { Folder } from '../../folders/domain/Folder'; import { FileNode } from './FileNode'; import { Node } from './Node'; + export class FolderNode { private constructor( public readonly folder: Folder, @@ -17,7 +18,7 @@ export class FolderNode { addChild(node: Node): void { if (this.children.has(node.id)) { - throw new Error('Child already exists'); + throw new Error(`Duplicated node detected: ${node.id}`); } this.children.set(node.id, node); diff --git a/tests/context/virtual-drive/items/application/Traverser.test.ts b/tests/context/virtual-drive/items/application/Traverser.test.ts index 62adf56d4..7c98bce10 100644 --- a/tests/context/virtual-drive/items/application/Traverser.test.ts +++ b/tests/context/virtual-drive/items/application/Traverser.test.ts @@ -8,10 +8,12 @@ import { } from '../../../../../src/context/shared/domain/ServerFolder'; import { Traverser } from '../../../../../src/context/virtual-drive/items/application/Traverser'; import { ContentsIdMother } from '../../contents/domain/ContentsIdMother'; +import { IpcRendererSyncEngineMock } from '../../shared/__mock__/IpcRendererSyncEngineMock'; import { FakeNameDecrypt } from '../infrastructure/FakeNameDecrypt'; describe('Traverser', () => { const nameDecrypt = new FakeNameDecrypt(); + const ipc = new IpcRendererSyncEngineMock(); it('first level files starts with /', () => { const baseFolderId = 6; @@ -29,6 +31,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -64,6 +67,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -91,6 +95,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -124,6 +129,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -157,6 +163,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -197,6 +204,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -224,6 +232,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -259,6 +268,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] @@ -294,6 +304,7 @@ describe('Traverser', () => { }; const SUT = new Traverser( nameDecrypt, + ipc, baseFolderId, [ServerFileStatus.EXISTS, ServerFileStatus.TRASHED], [ServerFolderStatus.EXISTS] From c9224c66caddeebfdc739a80eafdc8e17e6e0716 Mon Sep 17 00:00:00 2001 From: migueldev01 Date: Thu, 25 Jan 2024 12:16:32 -0500 Subject: [PATCH 5/8] Add polling monitor start and stop functionality --- src/apps/sync-engine/BindingManager.ts | 6 ++++ .../shared/SharedContainer.ts | 4 +++ .../dependency-injection/shared/builder.ts | 10 ++++++ .../shared/application/PollingMonitorStart.ts | 8 +++++ .../shared/application/PollingMonitorStop.ts | 8 +++++ .../shared/domain/PollingMonitor.ts | 31 +++++++++++++++++++ 6 files changed, 67 insertions(+) create mode 100644 src/context/virtual-drive/shared/application/PollingMonitorStart.ts create mode 100644 src/context/virtual-drive/shared/application/PollingMonitorStop.ts create mode 100644 src/context/virtual-drive/shared/domain/PollingMonitor.ts diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index 76c4ea410..39aa12c69 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -37,6 +37,7 @@ export class BindingsManager { async start(version: string, providerId: string) { await this.stop(); + this.container.pollingMonitorStart.run(this.monitorSimulateFn); const controllers = buildControllers(this.container); @@ -215,6 +216,7 @@ export class BindingsManager { async stop() { await this.container.virtualDrive.disconnectSyncRoot(); + this.container.pollingMonitorStop.run(); } async cleanUp() { @@ -276,4 +278,8 @@ export class BindingsManager { Logger.error('[SYNC ENGINE] ', error); } } + + private async monitorSimulateFn(): Promise { + Logger.info('[SYNC ENGINE] Starting monitoring polling...'); + } } diff --git a/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts b/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts index 1649eceac..1386789f7 100644 --- a/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts +++ b/src/apps/sync-engine/dependency-injection/shared/SharedContainer.ts @@ -1,7 +1,11 @@ import { AbsolutePathToRelativeConverter } from '../../../../context/virtual-drive/shared/application/AbsolutePathToRelativeConverter'; +import { PollingMonitorStart } from '../../../../context/virtual-drive/shared/application/PollingMonitorStart'; +import { PollingMonitorStop } from '../../../../context/virtual-drive/shared/application/PollingMonitorStop'; import { RelativePathToAbsoluteConverter } from '../../../../context/virtual-drive/shared/application/RelativePathToAbsoluteConverter'; export interface SharedContainer { absolutePathToRelativeConverter: AbsolutePathToRelativeConverter; relativePathToAbsoluteConverter: RelativePathToAbsoluteConverter; + pollingMonitorStart: PollingMonitorStart; + pollingMonitorStop: PollingMonitorStop; } diff --git a/src/apps/sync-engine/dependency-injection/shared/builder.ts b/src/apps/sync-engine/dependency-injection/shared/builder.ts index b7b086734..87923cbac 100644 --- a/src/apps/sync-engine/dependency-injection/shared/builder.ts +++ b/src/apps/sync-engine/dependency-injection/shared/builder.ts @@ -1,9 +1,13 @@ import { AbsolutePathToRelativeConverter } from '../../../../context/virtual-drive/shared/application/AbsolutePathToRelativeConverter'; +import { PollingMonitorStart } from '../../../../context/virtual-drive/shared/application/PollingMonitorStart'; +import { PollingMonitorStop } from '../../../../context/virtual-drive/shared/application/PollingMonitorStop'; import { RelativePathToAbsoluteConverter } from '../../../../context/virtual-drive/shared/application/RelativePathToAbsoluteConverter'; +import { PollingMonitor } from '../../../../context/virtual-drive/shared/domain/PollingMonitor'; import { DependencyInjectionLocalRootFolderPath } from '../common/localRootFolderPath'; import { SharedContainer } from './SharedContainer'; export function buildSharedContainer(): SharedContainer { + const MONITORING_PULLING_INTERVAL = 1_000; // 1 second const localRootFolderPath = DependencyInjectionLocalRootFolderPath.get(); const absolutePathToRelativeConverter = new AbsolutePathToRelativeConverter( localRootFolderPath @@ -13,8 +17,14 @@ export function buildSharedContainer(): SharedContainer { localRootFolderPath ); + const pollingMonitor = new PollingMonitor(MONITORING_PULLING_INTERVAL); + const pollingMonitorStart = new PollingMonitorStart(pollingMonitor); + const pollingMonitorStop = new PollingMonitorStop(pollingMonitor); + return { absolutePathToRelativeConverter, relativePathToAbsoluteConverter, + pollingMonitorStart, + pollingMonitorStop, }; } diff --git a/src/context/virtual-drive/shared/application/PollingMonitorStart.ts b/src/context/virtual-drive/shared/application/PollingMonitorStart.ts new file mode 100644 index 000000000..6c0f2918c --- /dev/null +++ b/src/context/virtual-drive/shared/application/PollingMonitorStart.ts @@ -0,0 +1,8 @@ +import { MonitorFn, PollingMonitor } from '../domain/PollingMonitor'; + +export class PollingMonitorStart { + constructor(private readonly polling: PollingMonitor) {} + run(fn: MonitorFn) { + return this.polling.start(fn); + } +} diff --git a/src/context/virtual-drive/shared/application/PollingMonitorStop.ts b/src/context/virtual-drive/shared/application/PollingMonitorStop.ts new file mode 100644 index 000000000..cdaee08c3 --- /dev/null +++ b/src/context/virtual-drive/shared/application/PollingMonitorStop.ts @@ -0,0 +1,8 @@ +import { PollingMonitor } from '../domain/PollingMonitor'; + +export class PollingMonitorStop { + constructor(private readonly polling: PollingMonitor) {} + run() { + return this.polling.stop(); + } +} diff --git a/src/context/virtual-drive/shared/domain/PollingMonitor.ts b/src/context/virtual-drive/shared/domain/PollingMonitor.ts new file mode 100644 index 000000000..9683e8386 --- /dev/null +++ b/src/context/virtual-drive/shared/domain/PollingMonitor.ts @@ -0,0 +1,31 @@ +export type MonitorFn = () => Promise; +export class PollingMonitor { + constructor( + private readonly delay: number // private readonly fn: () => Promise + ) {} + + private timeout: NodeJS.Timeout | null = null; + + private clearTimeout() { + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + } + + private setTimeout(fn: MonitorFn) { + this.clearTimeout(); + this.timeout = setTimeout(async () => { + await fn(); + this.setTimeout(fn); + }, this.delay); + } + + start(fn: MonitorFn) { + this.setTimeout(fn); + } + + stop() { + this.clearTimeout(); + } +} From 90c3aa54ceaed12719227fc34c4caa48ddb4b783 Mon Sep 17 00:00:00 2001 From: migueldev01 Date: Fri, 26 Jan 2024 09:39:15 -0500 Subject: [PATCH 6/8] Update polling interval in BindingManager and Builder --- src/apps/sync-engine/BindingManager.ts | 13 ++++++++++--- .../dependency-injection/shared/builder.ts | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index 39aa12c69..fc8b6d548 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -37,7 +37,7 @@ export class BindingsManager { async start(version: string, providerId: string) { await this.stop(); - this.container.pollingMonitorStart.run(this.monitorSimulateFn); + this.pillingStart(); const controllers = buildControllers(this.container); @@ -279,7 +279,14 @@ export class BindingsManager { } } - private async monitorSimulateFn(): Promise { - Logger.info('[SYNC ENGINE] Starting monitoring polling...'); + private async pillingStart() { + return this.container.pollingMonitorStart.run(this.pilling.bind(this)); + } + + private async pilling(): Promise { + Logger.info('[SYNC ENGINE] Monitoring polling...'); + const result = + await this.container.virtualDrive.getPlaceholderWithStatePending(); + Logger.info('[SYNC ENGINE] result', result); } } diff --git a/src/apps/sync-engine/dependency-injection/shared/builder.ts b/src/apps/sync-engine/dependency-injection/shared/builder.ts index 87923cbac..79046f40b 100644 --- a/src/apps/sync-engine/dependency-injection/shared/builder.ts +++ b/src/apps/sync-engine/dependency-injection/shared/builder.ts @@ -7,7 +7,7 @@ import { DependencyInjectionLocalRootFolderPath } from '../common/localRootFolde import { SharedContainer } from './SharedContainer'; export function buildSharedContainer(): SharedContainer { - const MONITORING_PULLING_INTERVAL = 1_000; // 1 second + const MONITORING_PULLING_INTERVAL = 5_000; // 1 second const localRootFolderPath = DependencyInjectionLocalRootFolderPath.get(); const absolutePathToRelativeConverter = new AbsolutePathToRelativeConverter( localRootFolderPath From 56d5de61da4af28494791e13c62a0a01868700b5 Mon Sep 17 00:00:00 2001 From: migueldev01 Date: Fri, 2 Feb 2024 14:54:05 -0500 Subject: [PATCH 7/8] update syncronizer --- src/apps/sync-engine/BindingManager.ts | 14 +- .../boundaryBridge/BoundaryBridgeContainer.ts | 2 + .../boundaryBridge/build.ts | 7 + .../files/FilesContainer.ts | 2 + .../dependency-injection/files/builder.ts | 11 ++ .../dependency-injection/shared/builder.ts | 2 +- .../application/FileSyncOrchestrator.ts | 20 +++ .../files/application/FileContentsUpdater.ts | 13 ++ .../files/application/FileCreator.ts | 20 ++- .../files/application/FileSyncronizer.ts | 120 ++++++++++++++++++ .../domain/file-systems/LocalFileSystem.ts | 2 + .../infrastructure/NodeWinLocalFileSystem.ts | 9 ++ .../infrastructure/dtos/ReplaceFileDTO.ts | 6 + .../shared/domain/PollingMonitor.ts | 4 +- .../files/__mocks__/LocalFileSystemMock.ts | 5 + 15 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts create mode 100644 src/context/virtual-drive/files/application/FileContentsUpdater.ts create mode 100644 src/context/virtual-drive/files/application/FileSyncronizer.ts create mode 100644 src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index fc8b6d548..eb2d9e2a0 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -37,7 +37,7 @@ export class BindingsManager { async start(version: string, providerId: string) { await this.stop(); - this.pillingStart(); + this.pollingStart(); const controllers = buildControllers(this.container); @@ -279,14 +279,14 @@ export class BindingsManager { } } - private async pillingStart() { - return this.container.pollingMonitorStart.run(this.pilling.bind(this)); + private async pollingStart() { + return this.container.pollingMonitorStart.run(this.polling.bind(this)); } - private async pilling(): Promise { + private async polling(): Promise { Logger.info('[SYNC ENGINE] Monitoring polling...'); - const result = - await this.container.virtualDrive.getPlaceholderWithStatePending(); - Logger.info('[SYNC ENGINE] result', result); + const fileInPendingPaths = + (await this.container.virtualDrive.getPlaceholderWithStatePending()) as Array; + await this.container.fileSyncOrchestrator.run(fileInPendingPaths); } } diff --git a/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts b/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts index a885d570f..eb12b6e6c 100644 --- a/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts +++ b/src/apps/sync-engine/dependency-injection/boundaryBridge/BoundaryBridgeContainer.ts @@ -1,5 +1,7 @@ import { FileCreationOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileCreationOrchestrator'; +import { FileSyncOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator'; export interface BoundaryBridgeContainer { fileCreationOrchestrator: FileCreationOrchestrator; + fileSyncOrchestrator: FileSyncOrchestrator; } diff --git a/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts b/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts index bb065d7e1..7afb7e851 100644 --- a/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts +++ b/src/apps/sync-engine/dependency-injection/boundaryBridge/build.ts @@ -2,6 +2,7 @@ import { BoundaryBridgeContainer } from './BoundaryBridgeContainer'; import { ContentsContainer } from '../contents/ContentsContainer'; import { FilesContainer } from '../files/FilesContainer'; import { FileCreationOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileCreationOrchestrator'; +import { FileSyncOrchestrator } from '../../../../context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator'; export function buildBoundaryBridgeContainer( contentsContainer: ContentsContainer, @@ -13,7 +14,13 @@ export function buildBoundaryBridgeContainer( filesContainer.sameFileWasMoved ); + const fileSyncOrchestrator = new FileSyncOrchestrator( + contentsContainer.contentsUploader, + filesContainer.fileSyncronizer + ); + return { fileCreationOrchestrator, + fileSyncOrchestrator, }; } diff --git a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts index 5ba634247..0106ae090 100644 --- a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts +++ b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts @@ -9,12 +9,14 @@ import { FilesPlaceholderCreator } from '../../../../context/virtual-drive/files import { RepositoryPopulator } from '../../../../context/virtual-drive/files/application/RepositoryPopulator'; import { RetrieveAllFiles } from '../../../../context/virtual-drive/files/application/RetrieveAllFiles'; import { SameFileWasMoved } from '../../../../context/virtual-drive/files/application/SameFileWasMoved'; +import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; export interface FilesContainer { fileFinderByContentsId: FileFinderByContentsId; fileDeleter: FileDeleter; filePathUpdater: FilePathUpdater; fileCreator: FileCreator; + fileSyncronizer: FileSyncronizer; filePlaceholderCreatorFromContentsId: FilePlaceholderCreatorFromContentsId; createFilePlaceholderOnDeletionFailed: CreateFilePlaceholderOnDeletionFailed; sameFileWasMoved: SameFileWasMoved; diff --git a/src/apps/sync-engine/dependency-injection/files/builder.ts b/src/apps/sync-engine/dependency-injection/files/builder.ts index e9cf5c424..6256d0a7a 100644 --- a/src/apps/sync-engine/dependency-injection/files/builder.ts +++ b/src/apps/sync-engine/dependency-injection/files/builder.ts @@ -24,6 +24,7 @@ import { SDKRemoteFileSystem } from '../../../../context/virtual-drive/files/inf import { NodeWinLocalFileSystem } from '../../../../context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem'; import { LocalFileIdProvider } from '../../../../context/virtual-drive/shared/application/LocalFileIdProvider'; import { DependencyInjectionHttpClientsProvider } from '../common/clients'; +import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; export async function buildFilesContainer( folderContainer: FoldersContainer, @@ -115,11 +116,21 @@ export async function buildFilesContainer( eventHistory ); + const fileSyncronizer = new FileSyncronizer( + repository, + localFileSystem, + fileCreator, + sharedContainer.absolutePathToRelativeConverter, + folderContainer.folderCreator, + folderContainer.offline.folderCreator + ); + const container: FilesContainer = { fileFinderByContentsId, fileDeleter, filePathUpdater, fileCreator, + fileSyncronizer, filePlaceholderCreatorFromContentsId: filePlaceholderCreatorFromContentsId, createFilePlaceholderOnDeletionFailed: createFilePlaceholderOnDeletionFailed, diff --git a/src/apps/sync-engine/dependency-injection/shared/builder.ts b/src/apps/sync-engine/dependency-injection/shared/builder.ts index 79046f40b..089719499 100644 --- a/src/apps/sync-engine/dependency-injection/shared/builder.ts +++ b/src/apps/sync-engine/dependency-injection/shared/builder.ts @@ -7,7 +7,7 @@ import { DependencyInjectionLocalRootFolderPath } from '../common/localRootFolde import { SharedContainer } from './SharedContainer'; export function buildSharedContainer(): SharedContainer { - const MONITORING_PULLING_INTERVAL = 5_000; // 1 second + const MONITORING_PULLING_INTERVAL = 60 * 60 * 1000; const localRootFolderPath = DependencyInjectionLocalRootFolderPath.get(); const absolutePathToRelativeConverter = new AbsolutePathToRelativeConverter( localRootFolderPath diff --git a/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts b/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts new file mode 100644 index 000000000..80e9afc13 --- /dev/null +++ b/src/context/virtual-drive/boundaryBridge/application/FileSyncOrchestrator.ts @@ -0,0 +1,20 @@ +import { RetryContentsUploader } from '../../contents/application/RetryContentsUploader'; +import { FileSyncronizer } from '../../files/application/FileSyncronizer'; + +export class FileSyncOrchestrator { + constructor( + private readonly contentsUploader: RetryContentsUploader, + private readonly fileSyncronizer: FileSyncronizer + ) {} + + run(absolutePaths: string[]): Promise { + return Promise.all( + absolutePaths.map(async (absolutePath) => { + await this.fileSyncronizer.run( + absolutePath, + this.contentsUploader.run.bind(this.contentsUploader) + ); + }) + ); + } +} diff --git a/src/context/virtual-drive/files/application/FileContentsUpdater.ts b/src/context/virtual-drive/files/application/FileContentsUpdater.ts new file mode 100644 index 000000000..e0a189bc9 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileContentsUpdater.ts @@ -0,0 +1,13 @@ +// import { FileRepository } from '../domain/FileRepository'; +// import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; + +// export class FileContentsUpdater { +// constructor( +// private readonly repository: FileRepository, +// private readonly remote: RemoteFileSystem +// ) {} + +// async run(file: File): Promise { +// await this.local.updateSyncStatus(file); +// } +// } diff --git a/src/context/virtual-drive/files/application/FileCreator.ts b/src/context/virtual-drive/files/application/FileCreator.ts index 3128d7828..ffab80019 100644 --- a/src/context/virtual-drive/files/application/FileCreator.ts +++ b/src/context/virtual-drive/files/application/FileCreator.ts @@ -22,15 +22,21 @@ export class FileCreator { private readonly ipc: SyncEngineIpc ) {} - async run(filePath: FilePath, contents: RemoteFileContents): Promise { + async run( + filePath: FilePath, + contents: RemoteFileContents, + existingFileAlreadyEvaluated = false + ): Promise { try { - const existingFile = this.repository.searchByPartial({ - path: PlatformPathConverter.winToPosix(filePath.value), - status: FileStatuses.EXISTS, - }); + if (!existingFileAlreadyEvaluated) { + const existingFile = this.repository.searchByPartial({ + path: PlatformPathConverter.winToPosix(filePath.value), + status: FileStatuses.EXISTS, + }); - if (existingFile) { - await this.fileDeleter.run(existingFile.contentsId); + if (existingFile) { + await this.fileDeleter.run(existingFile.contentsId); + } } const size = new FileSize(contents.size); diff --git a/src/context/virtual-drive/files/application/FileSyncronizer.ts b/src/context/virtual-drive/files/application/FileSyncronizer.ts new file mode 100644 index 000000000..93ed61687 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileSyncronizer.ts @@ -0,0 +1,120 @@ +import { RemoteFileContents } from '../../contents/domain/RemoteFileContents'; +import { PlatformPathConverter } from '../../shared/application/PlatformPathConverter'; +import { FilePath } from '../domain/FilePath'; +import { FileRepository } from '../domain/FileRepository'; +import { FileStatuses } from '../domain/FileStatus'; +import Logger from 'electron-log'; +import { FileCreator } from './FileCreator'; +import { AbsolutePathToRelativeConverter } from '../../shared/application/AbsolutePathToRelativeConverter'; +import { FolderNotFoundError } from '../../folders/domain/errors/FolderNotFoundError'; +import { FolderCreator } from '../../folders/application/FolderCreator'; +import { OfflineFolderCreator } from '../../folders/application/Offline/OfflineFolderCreator'; +import { Folder } from '../../folders/domain/Folder'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import * as fs from 'fs'; +import { File } from '../domain/File'; + +export class FileSyncronizer { + constructor( + private readonly repository: FileRepository, + private readonly localFileSystem: LocalFileSystem, + private readonly fileCreator: FileCreator, + private readonly absolutePathToRelativeConverter: AbsolutePathToRelativeConverter, + private readonly folderCreator: FolderCreator, + private readonly offlineFolderCreator: OfflineFolderCreator + ) {} + + async run( + absolutePath: string, + upload: (path: string) => Promise + ) { + const win32RelativePath = + this.absolutePathToRelativeConverter.run(absolutePath); + + const posixRelativePath = + PlatformPathConverter.winToPosix(win32RelativePath); + + const path = new FilePath(posixRelativePath); + + const existingFile = this.repository.searchByPartial({ + path: PlatformPathConverter.winToPosix(path.value), + status: FileStatuses.EXISTS, + }); + + Logger.debug(`Updating sync status file ${posixRelativePath}`); + if (existingFile) { + if (this.hasDifferentSize(existingFile, absolutePath)) { + Logger.debug( + `[${posixRelativePath}] has different size update content` + ); + return; + } + Logger.debug(`[${posixRelativePath}] already exists`); + await this.localFileSystem.updateSyncStatus(existingFile); + } else { + Logger.debug(`[${posixRelativePath}] does not exist`); + await this.retryCreation(posixRelativePath, path, upload); + } + } + + private async retryCreation( + posixRelativePath: string, + filePath: FilePath, + upload: (path: string) => Promise, + attemps = 3 + ) { + try { + const fileContents = await upload(posixRelativePath); + const createdFile = await this.fileCreator.run(filePath, fileContents); + await this.localFileSystem.updateSyncStatus(createdFile); + } catch (error: unknown) { + if (error instanceof FolderNotFoundError) { + await this.createFolderFather(posixRelativePath); + } + if (attemps > 0) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + await this.retryCreation( + posixRelativePath, + filePath, + upload, + attemps - 1 + ); + return; + } + } + } + + private async runFolderCreator(posixRelativePath: string): Promise { + const offlineFolder = this.offlineFolderCreator.run(posixRelativePath); + return this.folderCreator.run(offlineFolder); + } + + private async createFolderFather(posixRelativePath: string) { + Logger.info('posixRelativePath', posixRelativePath); + const posixDir = + PlatformPathConverter.getFatherPathPosix(posixRelativePath); + Logger.info('posixDir', posixDir); + try { + await this.runFolderCreator(posixDir); + } catch (error) { + Logger.error('Error creating folder father creation:', error); + if (error instanceof FolderNotFoundError) { + // father created + await this.createFolderFather(posixDir); + // child created + await this.runFolderCreator(posixDir); + } else { + Logger.error( + 'Error creating folder father creation inside catch:', + error + ); + throw error; + } + } + } + + private hasDifferentSize(file: File, absoulthePath: string) { + const stats = fs.statSync(absoulthePath); + return file.size !== stats.size; + } +} diff --git a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts index acdddbb1d..602e40a8c 100644 --- a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts @@ -4,4 +4,6 @@ export interface LocalFileSystem { createPlaceHolder(file: File): Promise; getLocalFileId(file: File): Promise<`${string}-${string}`>; + + updateSyncStatus(file: File): Promise; } diff --git a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts index 49e7e5cf1..27725ce86 100644 --- a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts @@ -4,6 +4,7 @@ import { File } from '../domain/File'; import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; import { RelativePathToAbsoluteConverter } from '../../shared/application/RelativePathToAbsoluteConverter'; import fs from 'fs/promises'; +import Logger from 'electron-log'; export class NodeWinLocalFileSystem implements LocalFileSystem { constructor( @@ -34,4 +35,12 @@ export class NodeWinLocalFileSystem implements LocalFileSystem { file.updatedAt.getTime() ); } + + async updateSyncStatus(file: File): Promise { + const win32AbsolutePath = this.relativePathToAbsoluteConverter.run( + file.path + ); + Logger.debug(`Updating sync status file ${win32AbsolutePath}`); + return this.virtualDrive.updateSyncStatus(win32AbsolutePath, false); + } } diff --git a/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts b/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts new file mode 100644 index 000000000..be1cd6457 --- /dev/null +++ b/src/context/virtual-drive/files/infrastructure/dtos/ReplaceFileDTO.ts @@ -0,0 +1,6 @@ +import { File } from '../../domain/File'; + +export interface ReplaceFileDTO { + fileId: File['contentsId']; + size: File['size']; +} diff --git a/src/context/virtual-drive/shared/domain/PollingMonitor.ts b/src/context/virtual-drive/shared/domain/PollingMonitor.ts index 9683e8386..ef232677a 100644 --- a/src/context/virtual-drive/shared/domain/PollingMonitor.ts +++ b/src/context/virtual-drive/shared/domain/PollingMonitor.ts @@ -1,8 +1,6 @@ export type MonitorFn = () => Promise; export class PollingMonitor { - constructor( - private readonly delay: number // private readonly fn: () => Promise - ) {} + constructor(private readonly delay: number) {} private timeout: NodeJS.Timeout | null = null; diff --git a/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts b/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts index 6495b0307..3ec74e1a0 100644 --- a/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts +++ b/tests/context/virtual-drive/files/__mocks__/LocalFileSystemMock.ts @@ -4,6 +4,7 @@ import { File } from '../../../../../src/context/virtual-drive/files/domain/File export class LocalFileSystemMock implements LocalFileSystem { public readonly createPlaceHolderMock = jest.fn(); public readonly getLocalFileIdMock = jest.fn(); + public readonly updateSyncStatusMock = jest.fn(); createPlaceHolder(file: File): Promise { return this.createPlaceHolder(file); @@ -12,4 +13,8 @@ export class LocalFileSystemMock implements LocalFileSystem { getLocalFileId(file: File): Promise<`${string}-${string}`> { return this.getLocalFileIdMock(file); } + + updateSyncStatus(file: File): Promise { + throw this.updateSyncStatusMock(file); + } } From a75c9b198224b6a469fa07cc5ea08914b60383e3 Mon Sep 17 00:00:00 2001 From: migueldev01 Date: Wed, 7 Feb 2024 21:29:36 -0500 Subject: [PATCH 8/8] add placeholder converter --- src/apps/sync-engine/BindingManager.ts | 3 +- .../controllers/AddController.ts | 1 + .../files/FilesContainer.ts | 4 +++ .../dependency-injection/files/builder.ts | 16 +++++++-- .../folders/FoldersContainer.ts | 6 ++++ .../dependency-injection/folders/builder.ts | 25 +++++++++++-- .../application/FIlePlaceholderConverter.ts | 10 ++++++ .../application/FileSyncStatusUpdater.ts | 10 ++++++ .../files/application/FileSyncronizer.ts | 36 ++++++++++--------- .../domain/file-systems/LocalFileSystem.ts | 2 ++ .../infrastructure/NodeWinLocalFileSystem.ts | 13 +++++-- .../folders/application/FolderCreator.ts | 6 +++- .../application/FolderPlaceholderConverter.ts | 10 ++++++ .../application/FolderSyncStatusUpdater.ts | 10 ++++++ .../FoldersFatherSyncStatusUpdater.ts | 30 ++++++++++++++++ .../domain/file-systems/LocalFileSystem.ts | 4 +++ .../infrastructure/NodeWinLocalFileSystem.ts | 26 +++++++++++++- 17 files changed, 187 insertions(+), 25 deletions(-) create mode 100644 src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts create mode 100644 src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts create mode 100644 src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts create mode 100644 src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts create mode 100644 src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index eb2d9e2a0..0c112941e 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -37,7 +37,7 @@ export class BindingsManager { async start(version: string, providerId: string) { await this.stop(); - this.pollingStart(); + await this.pollingStart(); const controllers = buildControllers(this.container); @@ -287,6 +287,7 @@ export class BindingsManager { Logger.info('[SYNC ENGINE] Monitoring polling...'); const fileInPendingPaths = (await this.container.virtualDrive.getPlaceholderWithStatePending()) as Array; + Logger.info('[SYNC ENGINE] fileInPendingPaths', fileInPendingPaths); await this.container.fileSyncOrchestrator.run(fileInPendingPaths); } } diff --git a/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts b/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts index c56cc9ecc..2645c6ce4 100644 --- a/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts +++ b/src/apps/sync-engine/callbacks-controllers/controllers/AddController.ts @@ -89,6 +89,7 @@ export class AddController extends CallbackController { const posixDir = PlatformPathConverter.getFatherPathPosix(posixRelativePath); try { + await new Promise((resolve) => setTimeout(resolve, 1000)); await this.runFolderCreator(posixDir); } catch (error) { Logger.error('Error creating folder father creation:', error); diff --git a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts index 0106ae090..12fa6eab3 100644 --- a/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts +++ b/src/apps/sync-engine/dependency-injection/files/FilesContainer.ts @@ -10,6 +10,8 @@ import { RepositoryPopulator } from '../../../../context/virtual-drive/files/app import { RetrieveAllFiles } from '../../../../context/virtual-drive/files/application/RetrieveAllFiles'; import { SameFileWasMoved } from '../../../../context/virtual-drive/files/application/SameFileWasMoved'; import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; +import { FilePlaceholderConverter } from '../../../../context/virtual-drive/files/application/FIlePlaceholderConverter'; +import { FileSyncStatusUpdater } from '../../../../context/virtual-drive/files/application/FileSyncStatusUpdater'; export interface FilesContainer { fileFinderByContentsId: FileFinderByContentsId; @@ -24,4 +26,6 @@ export interface FilesContainer { repositoryPopulator: RepositoryPopulator; filesPlaceholderCreator: FilesPlaceholderCreator; filesPlaceholderUpdater: FilesPlaceholderUpdater; + filePlaceholderConverter: FilePlaceholderConverter; + fileSyncStatusUpdater: FileSyncStatusUpdater; } diff --git a/src/apps/sync-engine/dependency-injection/files/builder.ts b/src/apps/sync-engine/dependency-injection/files/builder.ts index 6256d0a7a..63f758eee 100644 --- a/src/apps/sync-engine/dependency-injection/files/builder.ts +++ b/src/apps/sync-engine/dependency-injection/files/builder.ts @@ -25,6 +25,8 @@ import { NodeWinLocalFileSystem } from '../../../../context/virtual-drive/files/ import { LocalFileIdProvider } from '../../../../context/virtual-drive/shared/application/LocalFileIdProvider'; import { DependencyInjectionHttpClientsProvider } from '../common/clients'; import { FileSyncronizer } from '../../../../context/virtual-drive/files/application/FileSyncronizer'; +import { FilePlaceholderConverter } from '../../../../context/virtual-drive/files/application/FIlePlaceholderConverter'; +import { FileSyncStatusUpdater } from '../../../../context/virtual-drive/files/application/FileSyncStatusUpdater'; export async function buildFilesContainer( folderContainer: FoldersContainer, @@ -116,13 +118,21 @@ export async function buildFilesContainer( eventHistory ); + const filePlaceholderConverter = new FilePlaceholderConverter( + localFileSystem + ); + + const fileSyncStatusUpdater = new FileSyncStatusUpdater(localFileSystem); + const fileSyncronizer = new FileSyncronizer( repository, - localFileSystem, + fileSyncStatusUpdater, + filePlaceholderConverter, fileCreator, sharedContainer.absolutePathToRelativeConverter, folderContainer.folderCreator, - folderContainer.offline.folderCreator + folderContainer.offline.folderCreator, + folderContainer.foldersFatherSyncStatusUpdater ); const container: FilesContainer = { @@ -139,6 +149,8 @@ export async function buildFilesContainer( repositoryPopulator: repositoryPopulator, filesPlaceholderCreator, filesPlaceholderUpdater, + filePlaceholderConverter, + fileSyncStatusUpdater, }; return { container, subscribers: [] }; diff --git a/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts b/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts index bcb774e3d..de4e028f0 100644 --- a/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts +++ b/src/apps/sync-engine/dependency-injection/folders/FoldersContainer.ts @@ -12,6 +12,9 @@ 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 { FolderPlaceholderConverter } from '../../../../context/virtual-drive/folders/application/FolderPlaceholderConverter'; +import { FolderSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FolderSyncStatusUpdater'; +import { FoldersFatherSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater'; export interface FoldersContainer { folderCreator: FolderCreator; @@ -30,4 +33,7 @@ export interface FoldersContainer { folderRepositoryInitiator: FolderRepositoryInitiator; folderPlaceholderUpdater: FolderPlaceholderUpdater; foldersPlaceholderCreator: FoldersPlaceholderCreator; + folderPlaceholderConverter: FolderPlaceholderConverter; + folderSyncStatusUpdater: FolderSyncStatusUpdater; + foldersFatherSyncStatusUpdater: FoldersFatherSyncStatusUpdater; } diff --git a/src/apps/sync-engine/dependency-injection/folders/builder.ts b/src/apps/sync-engine/dependency-injection/folders/builder.ts index b40d16044..233dd2b74 100644 --- a/src/apps/sync-engine/dependency-injection/folders/builder.ts +++ b/src/apps/sync-engine/dependency-injection/folders/builder.ts @@ -27,6 +27,9 @@ import { DependencyInjectionEventRepository } from '../common/eventRepository'; import { DependencyInjectionVirtualDrive } from '../common/virtualDrive'; import { SharedContainer } from '../shared/SharedContainer'; import { FoldersContainer } from './FoldersContainer'; +import { FolderPlaceholderConverter } from '../../../../context/virtual-drive/folders/application/FolderPlaceholderConverter'; +import { FolderSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FolderSyncStatusUpdater'; +import { FoldersFatherSyncStatusUpdater } from '../../../../context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater'; export async function buildFoldersContainer( shredContainer: SharedContainer @@ -38,12 +41,21 @@ export async function buildFoldersContainer( const repository = new InMemoryFolderRepository(); - const localFileSystem = new NodeWinLocalFileSystem(virtualDrive); + const localFileSystem = new NodeWinLocalFileSystem( + virtualDrive, + shredContainer.relativePathToAbsoluteConverter + ); const remoteFileSystem = new HttpRemoteFileSystem( clients.drive, clients.newDrive ); + const folderPlaceholderConverter = new FolderPlaceholderConverter( + localFileSystem + ); + + const folderSyncStatusUpdater = new FolderSyncStatusUpdater(localFileSystem); + const folderFinder = new FolderFinder(repository); const allParentFoldersStatusIsExists = new AllParentFoldersStatusIsExists( @@ -61,7 +73,8 @@ export async function buildFoldersContainer( repository, remoteFileSystem, ipcRendererSyncEngine, - eventBus + eventBus, + folderPlaceholderConverter ); const folderMover = new FolderMover( @@ -124,6 +137,11 @@ export async function buildFoldersContainer( shredContainer.relativePathToAbsoluteConverter ); + const foldersFatherSyncStatusUpdater = new FoldersFatherSyncStatusUpdater( + localFileSystem, + repository + ); + return { folderCreator, folderFinder, @@ -141,5 +159,8 @@ export async function buildFoldersContainer( folderRepositoryInitiator, foldersPlaceholderCreator, folderPlaceholderUpdater, + folderPlaceholderConverter, + folderSyncStatusUpdater, + foldersFatherSyncStatusUpdater, }; } diff --git a/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts b/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts new file mode 100644 index 000000000..763a52482 --- /dev/null +++ b/src/context/virtual-drive/files/application/FIlePlaceholderConverter.ts @@ -0,0 +1,10 @@ +import { File } from '../domain/File'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FilePlaceholderConverter { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(file: File) { + await this.localFileSystem.convertToPlaceholder(file); + } +} diff --git a/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts b/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts new file mode 100644 index 000000000..a03a284e8 --- /dev/null +++ b/src/context/virtual-drive/files/application/FileSyncStatusUpdater.ts @@ -0,0 +1,10 @@ +import { File } from '../domain/File'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FileSyncStatusUpdater { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(file: File) { + await this.localFileSystem.updateSyncStatus(file); + } +} diff --git a/src/context/virtual-drive/files/application/FileSyncronizer.ts b/src/context/virtual-drive/files/application/FileSyncronizer.ts index 93ed61687..f38038945 100644 --- a/src/context/virtual-drive/files/application/FileSyncronizer.ts +++ b/src/context/virtual-drive/files/application/FileSyncronizer.ts @@ -10,18 +10,22 @@ import { FolderNotFoundError } from '../../folders/domain/errors/FolderNotFoundE import { FolderCreator } from '../../folders/application/FolderCreator'; import { OfflineFolderCreator } from '../../folders/application/Offline/OfflineFolderCreator'; import { Folder } from '../../folders/domain/Folder'; -import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; import * as fs from 'fs'; import { File } from '../domain/File'; +import { FileSyncStatusUpdater } from './FileSyncStatusUpdater'; +import { FilePlaceholderConverter } from './FIlePlaceholderConverter'; +import { FoldersFatherSyncStatusUpdater } from '../../folders/application/FoldersFatherSyncStatusUpdater'; export class FileSyncronizer { constructor( private readonly repository: FileRepository, - private readonly localFileSystem: LocalFileSystem, + private readonly fileSyncStatusUpdater: FileSyncStatusUpdater, + private readonly filePlaceholderConverter: FilePlaceholderConverter, private readonly fileCreator: FileCreator, private readonly absolutePathToRelativeConverter: AbsolutePathToRelativeConverter, private readonly folderCreator: FolderCreator, - private readonly offlineFolderCreator: OfflineFolderCreator + private readonly offlineFolderCreator: OfflineFolderCreator, + private readonly foldersFatherSyncStatusUpdater: FoldersFatherSyncStatusUpdater ) {} async run( @@ -41,33 +45,28 @@ export class FileSyncronizer { status: FileStatuses.EXISTS, }); - Logger.debug(`Updating sync status file ${posixRelativePath}`); if (existingFile) { if (this.hasDifferentSize(existingFile, absolutePath)) { - Logger.debug( - `[${posixRelativePath}] has different size update content` - ); return; } - Logger.debug(`[${posixRelativePath}] already exists`); - await this.localFileSystem.updateSyncStatus(existingFile); + await this.convertAndUpdateSyncStatus(existingFile); } else { - Logger.debug(`[${posixRelativePath}] does not exist`); await this.retryCreation(posixRelativePath, path, upload); } } - private async retryCreation( + private retryCreation = async ( posixRelativePath: string, filePath: FilePath, upload: (path: string) => Promise, attemps = 3 - ) { + ) => { try { const fileContents = await upload(posixRelativePath); const createdFile = await this.fileCreator.run(filePath, fileContents); - await this.localFileSystem.updateSyncStatus(createdFile); + await this.convertAndUpdateSyncStatus(createdFile); } catch (error: unknown) { + Logger.error('Error creating file:', error); if (error instanceof FolderNotFoundError) { await this.createFolderFather(posixRelativePath); } @@ -82,7 +81,7 @@ export class FileSyncronizer { return; } } - } + }; private async runFolderCreator(posixRelativePath: string): Promise { const offlineFolder = this.offlineFolderCreator.run(posixRelativePath); @@ -93,8 +92,8 @@ export class FileSyncronizer { Logger.info('posixRelativePath', posixRelativePath); const posixDir = PlatformPathConverter.getFatherPathPosix(posixRelativePath); - Logger.info('posixDir', posixDir); try { + await new Promise((resolve) => setTimeout(resolve, 1000)); await this.runFolderCreator(posixDir); } catch (error) { Logger.error('Error creating folder father creation:', error); @@ -115,6 +114,11 @@ export class FileSyncronizer { private hasDifferentSize(file: File, absoulthePath: string) { const stats = fs.statSync(absoulthePath); - return file.size !== stats.size; + return Math.abs(file.size - stats.size) > 0.001; + } + + private async convertAndUpdateSyncStatus(file: File) { + await this.filePlaceholderConverter.run(file); + await this.fileSyncStatusUpdater.run(file); } } diff --git a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts index 602e40a8c..a7fb66102 100644 --- a/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/files/domain/file-systems/LocalFileSystem.ts @@ -6,4 +6,6 @@ export interface LocalFileSystem { getLocalFileId(file: File): Promise<`${string}-${string}`>; updateSyncStatus(file: File): Promise; + + convertToPlaceholder(file: File): Promise; } diff --git a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts index 27725ce86..21682a31f 100644 --- a/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/files/infrastructure/NodeWinLocalFileSystem.ts @@ -4,7 +4,6 @@ import { File } from '../domain/File'; import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; import { RelativePathToAbsoluteConverter } from '../../shared/application/RelativePathToAbsoluteConverter'; import fs from 'fs/promises'; -import Logger from 'electron-log'; export class NodeWinLocalFileSystem implements LocalFileSystem { constructor( @@ -40,7 +39,17 @@ export class NodeWinLocalFileSystem implements LocalFileSystem { const win32AbsolutePath = this.relativePathToAbsoluteConverter.run( file.path ); - Logger.debug(`Updating sync status file ${win32AbsolutePath}`); return this.virtualDrive.updateSyncStatus(win32AbsolutePath, false); } + + async convertToPlaceholder(file: File): Promise { + const win32AbsolutePath = this.relativePathToAbsoluteConverter.run( + file.path + ); + + return this.virtualDrive.convertToPlaceholder( + win32AbsolutePath, + file.placeholderId + ); + } } diff --git a/src/context/virtual-drive/folders/application/FolderCreator.ts b/src/context/virtual-drive/folders/application/FolderCreator.ts index 1e3a83f36..0b64008dc 100644 --- a/src/context/virtual-drive/folders/application/FolderCreator.ts +++ b/src/context/virtual-drive/folders/application/FolderCreator.ts @@ -4,13 +4,15 @@ import { Folder } from '../domain/Folder'; import { FolderRepository } from '../domain/FolderRepository'; import { OfflineFolder } from '../domain/OfflineFolder'; import { RemoteFileSystem } from '../domain/file-systems/RemoteFileSystem'; +import { FolderPlaceholderConverter } from './FolderPlaceholderConverter'; export class FolderCreator { constructor( private readonly repository: FolderRepository, private readonly remote: RemoteFileSystem, private readonly ipc: SyncEngineIpc, - private readonly eventBus: EventBus + private readonly eventBus: EventBus, + private readonly folderPlaceholderConverter: FolderPlaceholderConverter ) {} async run(offlineFolder: OfflineFolder): Promise { @@ -27,6 +29,8 @@ export class FolderCreator { const events = folder.pullDomainEvents(); this.eventBus.publish(events); + await this.folderPlaceholderConverter.run(folder); + this.ipc.send('FOLDER_CREATED', { name: offlineFolder.name, }); diff --git a/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts b/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts new file mode 100644 index 000000000..b3d84f30c --- /dev/null +++ b/src/context/virtual-drive/folders/application/FolderPlaceholderConverter.ts @@ -0,0 +1,10 @@ +import { Folder } from '../domain/Folder'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FolderPlaceholderConverter { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(folder: Folder) { + await this.localFileSystem.convertToPlaceholder(folder); + } +} diff --git a/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts b/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts new file mode 100644 index 000000000..d333caf4c --- /dev/null +++ b/src/context/virtual-drive/folders/application/FolderSyncStatusUpdater.ts @@ -0,0 +1,10 @@ +import { Folder } from '../domain/Folder'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; + +export class FolderSyncStatusUpdater { + constructor(private readonly localFileSystem: LocalFileSystem) {} + + async run(folder: Folder) { + await this.localFileSystem.updateSyncStatus(folder); + } +} diff --git a/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts b/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts new file mode 100644 index 000000000..6b94883df --- /dev/null +++ b/src/context/virtual-drive/folders/application/FoldersFatherSyncStatusUpdater.ts @@ -0,0 +1,30 @@ +import { File } from '../../files/domain/File'; +import { PlatformPathConverter } from '../../shared/application/PlatformPathConverter'; +import { Folder } from '../domain/Folder'; +import { FolderRepository } from '../domain/FolderRepository'; +import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import Logger from 'electron-log'; + +export class FoldersFatherSyncStatusUpdater { + constructor( + private readonly localFileSystem: LocalFileSystem, + private readonly repository: FolderRepository + ) {} + + async run(file: File): Promise { + return this.update(file.path); + } + + private async update(path: File['path'] | Folder['path']) { + const posixDir = PlatformPathConverter.getFatherPathPosix(path); + if (posixDir === '/') { + return; + } + const folder = await this.repository.searchByPartial({ path: posixDir }); + if (folder) { + Logger.debug(`Updating sync status for ${folder.path}`); + await this.localFileSystem.updateSyncStatus(folder); + } + this.update(posixDir); + } +} diff --git a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts b/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts index b16434a46..e686e68ca 100644 --- a/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts +++ b/src/context/virtual-drive/folders/domain/file-systems/LocalFileSystem.ts @@ -2,4 +2,8 @@ import { Folder } from '../Folder'; export interface LocalFileSystem { createPlaceHolder(folder: Folder): Promise; + + updateSyncStatus(folder: Folder): Promise; + + convertToPlaceholder(folder: Folder): Promise; } diff --git a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts index 1c1a2d5b2..f644ea8fa 100644 --- a/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts +++ b/src/context/virtual-drive/folders/infrastructure/NodeWinLocalFileSystem.ts @@ -2,9 +2,13 @@ import { VirtualDrive } from 'virtual-drive/dist'; import { Folder } from '../domain/Folder'; import { FolderStatuses } from '../domain/FolderStatus'; import { LocalFileSystem } from '../domain/file-systems/LocalFileSystem'; +import { RelativePathToAbsoluteConverter } from '../../shared/application/RelativePathToAbsoluteConverter'; export class NodeWinLocalFileSystem implements LocalFileSystem { - constructor(private readonly virtualDrive: VirtualDrive) {} + constructor( + private readonly virtualDrive: VirtualDrive, + private readonly relativePathToAbsoluteConverter: RelativePathToAbsoluteConverter + ) {} async createPlaceHolder(folder: Folder): Promise { if (!folder.hasStatus(FolderStatuses.EXISTS)) { @@ -21,4 +25,24 @@ export class NodeWinLocalFileSystem implements LocalFileSystem { folder.updatedAt.getTime() ); } + + async updateSyncStatus(folder: Folder): Promise { + const folderPath = `${folder.path}/`; + const win32AbsolutePath = + this.relativePathToAbsoluteConverter.run(folderPath); + + return this.virtualDrive.updateSyncStatus(win32AbsolutePath, true); + } + + async convertToPlaceholder(folder: Folder): Promise { + const folderPath = `${folder.path}/`; + + const win32AbsolutePath = + this.relativePathToAbsoluteConverter.run(folderPath); + + return this.virtualDrive.convertToPlaceholder( + win32AbsolutePath, + folder.placeholderId + ); + } }