diff --git a/src/apps/main/remote-sync/RemoteSyncManager.ts b/src/apps/main/remote-sync/RemoteSyncManager.ts index 33c0d9808..33ff38fc6 100644 --- a/src/apps/main/remote-sync/RemoteSyncManager.ts +++ b/src/apps/main/remote-sync/RemoteSyncManager.ts @@ -16,6 +16,9 @@ import { Axios } from 'axios'; import { DriveFolder } from '../database/entities/DriveFolder'; import { DriveFile } from '../database/entities/DriveFile'; import { Nullable } from '../../shared/types/Nullable'; +import configStore from '../config'; +import { result } from 'lodash'; +import { convertUTCToSpain } from '../../utils/date'; export class RemoteSyncManager { private foldersSyncStatus: RemoteSyncStatus = 'IDLE'; @@ -109,16 +112,19 @@ export class RemoteSyncManager { * * Throws an error if there's a sync in progress for this class instance */ - async startRemoteSync() { + async startRemoteSync(folderId?: number) { // const start = Date.now(); Logger.info('Starting remote to local sync'); Logger.info('Checking if we are in a valid state to start the sync'); - const testPassed = this.smokeTest(); + // const testPassed = this.smokeTest(); - if (!testPassed) { - return; - } + // if (!testPassed) { + // return { + // files: [], + // folders: [], + // }; + // } this.totalFilesSynced = 0; this.totalFilesUnsynced = []; this.totalFoldersSynced = 0; @@ -128,20 +134,43 @@ export class RemoteSyncManager { Logger.info('Starting RemoteSyncManager'); this.changeStatus('SYNCING'); try { - await Promise.all([ - this.config.syncFiles - ? this.syncRemoteFiles({ - retry: 1, - maxRetries: 3, - }) - : Promise.resolve(), - this.config.syncFolders - ? this.syncRemoteFolders({ - retry: 1, - maxRetries: 3, - }) - : Promise.resolve(), + // await Promise.all([ + // this.config.syncFiles + // ? this.syncRemoteFiles({ + // retry: 1, + // maxRetries: 3, + // }) + // : Promise.resolve(), + // this.config.syncFolders + // ? this.syncRemoteFolders({ + // retry: 1, + // maxRetries: 3, + // }) + // : Promise.resolve(), + // ]); + // const userData = configStore.get('userData'); + const syncOptions = { + retry: 1, + maxRetries: 3, + }; + + const syncFilesPromise = this.config.syncFiles + ? folderId + ? this.syncRemoteFilesByFolder(syncOptions, folderId) + : this.syncRemoteFiles(syncOptions) + : Promise.resolve(); + + const syncFoldersPromise = this.config.syncFolders + ? folderId + ? this.syncRemoteFoldersByFolder(syncOptions, folderId) + : this.syncRemoteFolders(syncOptions) + : Promise.resolve(); + + const [_files, folders] = await Promise.all([ + syncFilesPromise, + syncFoldersPromise, ]); + return { files: _files, folders }; } catch (error) { this.changeStatus('SYNC_FAILED'); reportError(error as Error); @@ -150,6 +179,10 @@ export class RemoteSyncManager { Logger.info('Total unsynced files: ', this.totalFilesUnsynced); Logger.info('Total synced folders: ', this.totalFoldersSynced); } + return { + files: [], + folders: [], + }; } /** @@ -254,17 +287,20 @@ export class RemoteSyncManager { * @param syncConfig Config to execute the sync with * @returns */ - private async syncRemoteFiles(syncConfig: SyncConfig, from?: Date) { + private async syncRemoteFiles( + syncConfig: SyncConfig, + from?: Date + ): Promise { const fileCheckpoint = from ?? (await this.getFileCheckpoint()); try { Logger.info( `Syncing files updated from ${ - fileCheckpoint ?? '(no last date provided)' + fileCheckpoint + ? fileCheckpoint?.toISOString() + : '(no last date provided)' }` ); - const { hasMore, result } = await this.fetchFilesFromRemote( - fileCheckpoint - ); + const { hasMore, result } = await this.fetchFilesFromRemote(); let lastFileSynced = null; @@ -280,10 +316,10 @@ export class RemoteSyncManager { Logger.info('Remote files sync finished'); this.filesSyncStatus = 'SYNCED'; this.checkRemoteSyncStatus(); - return; + return result; } Logger.info('Retrieving more files for sync'); - await this.syncRemoteFiles( + return await this.syncRemoteFiles( { retry: 1, maxRetries: syncConfig.maxRetries, @@ -305,12 +341,80 @@ export class RemoteSyncManager { return; } - await this.syncRemoteFiles({ + return await this.syncRemoteFiles({ retry: syncConfig.retry + 1, maxRetries: syncConfig.maxRetries, }); } } + private async syncRemoteFilesByFolder( + syncConfig: SyncConfig, + folderId: number, + from?: Date + ): Promise { + const fileCheckpoint = from ?? (await this.getFileCheckpoint()); + try { + Logger.info( + `Syncing files updated from ${ + fileCheckpoint + ? fileCheckpoint?.toISOString() + : '(no last date provided)' + }` + ); + const { hasMore, result } = await this.fetchFilesByFolderFromRemote( + folderId, + fileCheckpoint + ); + + let lastFileSynced = null; + + for (const remoteFile of result) { + // eslint-disable-next-line no-await-in-loop + await this.createOrUpdateSyncedFileEntry(remoteFile); + + this.totalFilesSynced++; + lastFileSynced = remoteFile; + } + + if (!hasMore) { + Logger.info('Remote files sync finished'); + this.filesSyncStatus = 'SYNCED'; + this.checkRemoteSyncStatus(); + return result; + } + Logger.info('Retrieving more files for sync'); + return await this.syncRemoteFilesByFolder( + { + retry: 1, + maxRetries: syncConfig.maxRetries, + }, + folderId, + lastFileSynced ? new Date(lastFileSynced.updatedAt) : undefined + ); + } catch (error) { + Logger.error('Remote files sync failed with error: ', error); + + reportError(error as Error, { + lastFilesSyncAt: fileCheckpoint + ? fileCheckpoint.toISOString() + : 'INITIAL_FILES_SYNC', + }); + if (syncConfig.retry >= syncConfig.maxRetries) { + // No more retries allowed, + this.filesSyncStatus = 'SYNC_FAILED'; + this.checkRemoteSyncStatus(); + return; + } + + return await this.syncRemoteFilesByFolder( + { + retry: syncConfig.retry + 1, + maxRetries: syncConfig.maxRetries, + }, + folderId + ); + } + } private async getLastFolderSyncAt(): Promise> { const { success, result } = await this.db.folders.getLastUpdated(); @@ -329,12 +433,17 @@ export class RemoteSyncManager { * @param syncConfig Config to execute the sync with * @returns */ - private async syncRemoteFolders(syncConfig: SyncConfig, from?: Date) { + private async syncRemoteFolders( + syncConfig: SyncConfig, + from?: Date + ): Promise { const lastFolderSyncAt = from ?? (await this.getLastFolderSyncAt()); try { Logger.info( `Syncing folders updated from ${ - lastFolderSyncAt ?? '(no last date provided)' + lastFolderSyncAt + ? lastFolderSyncAt?.toISOString() + : '(no last date provided)' }` ); const { hasMore, result } = await this.fetchFoldersFromRemote( @@ -354,11 +463,11 @@ export class RemoteSyncManager { if (!hasMore) { this.foldersSyncStatus = 'SYNCED'; this.checkRemoteSyncStatus(); - return; + return result; } Logger.info('Retrieving more folders for sync'); - await this.syncRemoteFolders( + return await this.syncRemoteFolders( { retry: 1, maxRetries: syncConfig.maxRetries, @@ -379,12 +488,79 @@ export class RemoteSyncManager { return; } - await this.syncRemoteFolders({ + return await this.syncRemoteFolders({ retry: syncConfig.retry + 1, maxRetries: syncConfig.maxRetries, }); } } + private async syncRemoteFoldersByFolder( + syncConfig: SyncConfig, + folderId: number, + from?: Date + ): Promise { + const lastFolderSyncAt = from ?? (await this.getLastFolderSyncAt()); + try { + Logger.info( + `Syncing folders updated from ${ + lastFolderSyncAt + ? lastFolderSyncAt?.toISOString() + : '(no last date provided)' + }` + ); + const { hasMore, result } = await this.fetchFoldersByFolderFromRemote( + folderId, + lastFolderSyncAt + ); + + let lastFolderSynced = null; + + for (const remoteFolder of result) { + // eslint-disable-next-line no-await-in-loop + await this.createOrUpdateSyncedFolderEntry(remoteFolder); + + this.totalFoldersSynced++; + lastFolderSynced = remoteFolder; + } + + if (!hasMore) { + this.foldersSyncStatus = 'SYNCED'; + this.checkRemoteSyncStatus(); + return result; + } + + Logger.info('Retrieving more folders for sync'); + return await this.syncRemoteFoldersByFolder( + { + retry: 1, + maxRetries: syncConfig.maxRetries, + }, + folderId, + lastFolderSynced ? new Date(lastFolderSynced.updatedAt) : undefined + ); + } catch (error) { + Logger.error('Remote folders sync failed with error: ', error); + reportError(error as Error, { + lastFoldersSyncAt: lastFolderSyncAt + ? lastFolderSyncAt.toISOString() + : 'INITIAL_FOLDERS_SYNC', + }); + if (syncConfig.retry >= syncConfig.maxRetries) { + // No more retries allowed, + this.foldersSyncStatus = 'SYNC_FAILED'; + this.checkRemoteSyncStatus(); + return; + } + + return await this.syncRemoteFoldersByFolder( + { + retry: syncConfig.retry + 1, + maxRetries: syncConfig.maxRetries, + }, + folderId + ); + } + } /** * Fetch the files that were updated after the given date @@ -440,6 +616,71 @@ export class RemoteSyncManager { }; } + /** + * Fetch the files that were updated after the given date + * + * @param updatedAtCheckpoint Retrieve files that were updated after this date + */ + private async fetchFilesByFolderFromRemote( + folderId: number, + updatedAtCheckpoint?: Date + ): Promise<{ + hasMore: boolean; + result: RemoteSyncedFile[]; + }> { + const params = { + limit: this.config.fetchFilesLimitPerRequest, + offset: 0, + status: 'ALL', + updatedAt: updatedAtCheckpoint + ? updatedAtCheckpoint.toISOString() + : undefined, + }; + const allFilesResponse = await this.fetchItemsByFolder( + params, + folderId, + 'files' + ); + if (allFilesResponse.status > 299) { + throw new Error( + `Fetch files response not ok with body ${JSON.stringify( + allFilesResponse.data, + null, + 2 + )} and status ${allFilesResponse.status}` + ); + } + + if (Array.isArray(allFilesResponse.data.result)) { + Logger.info( + `Received ${allFilesResponse.data.result.length} fetched files` + ); + } else { + // Logger.info( + // `Expected to receive an array of files, but instead received ${JSON.stringify( + // allFilesResponse, + // null, + // 2 + // )}` + // ); + + throw new Error('Did not receive an array of files'); + } + + const hasMore = + allFilesResponse.data.result.length === + this.config.fetchFilesLimitPerRequest; + + return { + hasMore, + result: + allFilesResponse.data.result && + Array.isArray(allFilesResponse.data.result) + ? allFilesResponse.data.result.map(this.patchDriveFileResponseItem) + : [], + }; + } + private fetchItems = async ( params: { limit: number; @@ -449,10 +690,42 @@ export class RemoteSyncManager { }, type: 'files' | 'folders' ) => { - return await this.config.httpClient.get( + const response = await this.config.httpClient.get( `${process.env.NEW_DRIVE_URL}/drive/${type}`, { params } ); + Logger.info( + `Fetching item ${type} response: ${JSON.stringify( + response.data[0], + null, + 2 + )}` + ); + return response; + }; + + private fetchItemsByFolder = async ( + params: { + limit: number; + offset: number; + status: string; + updatedAt: string | undefined; + }, + folderId: number, + type: 'files' | 'folders' + ) => { + const response = await this.config.httpClient.get( + `${process.env.NEW_DRIVE_URL}/drive/folders/${folderId}/${type}`, + { params } + ); + Logger.info( + `Fetching by folder ${type} by folder response: ${JSON.stringify( + response.data.result[0], + null, + 2 + )}` + ); + return response; }; /** @@ -510,6 +783,69 @@ export class RemoteSyncManager { : [], }; } + private async fetchFoldersByFolderFromRemote( + folderId: number, + updatedAtCheckpoint?: Date + ): Promise<{ + hasMore: boolean; + result: RemoteSyncedFolder[]; + }> { + const params = { + limit: this.config.fetchFilesLimitPerRequest, + offset: 0, + status: 'ALL', + updatedAt: updatedAtCheckpoint + ? updatedAtCheckpoint.toISOString() + : undefined, + }; + + const allFoldersResponse = await this.fetchItemsByFolder( + params, + folderId, + 'folders' + ); + + if (allFoldersResponse.status > 299) { + throw new Error( + `Fetch files response not ok with body ${JSON.stringify( + allFoldersResponse.data.result, + null, + 2 + )} and status ${allFoldersResponse.status}` + ); + } + + if (Array.isArray(allFoldersResponse.data.result)) { + Logger.info( + `Received ${allFoldersResponse.data.result.length} fetched files` + ); + } else { + Logger.info( + `Expected to receive an array of files, but instead received ${JSON.stringify( + allFoldersResponse.status, + null, + 2 + )}` + ); + + throw new Error('Did not receive an array of files'); + } + + const hasMore = + allFoldersResponse.data.result.length === + this.config.fetchFilesLimitPerRequest; + + return { + hasMore, + result: + allFoldersResponse.data.result && + Array.isArray(allFoldersResponse.data.result) + ? allFoldersResponse.data.result.map( + this.patchDriveFolderResponseItem + ) + : [], + }; + } private patchDriveFolderResponseItem = (payload: any): RemoteSyncedFolder => { // We will assume that we received an status diff --git a/src/apps/main/remote-sync/handlers.ts b/src/apps/main/remote-sync/handlers.ts index 85c2fdc34..e4c1fe2c3 100644 --- a/src/apps/main/remote-sync/handlers.ts +++ b/src/apps/main/remote-sync/handlers.ts @@ -15,10 +15,10 @@ import { sendUpdateFilesInSyncPending, } from '../background-processes/sync-engine'; import { debounce } from 'lodash'; +import configStore from '../config'; const SYNC_DEBOUNCE_DELAY = 3_000; - let initialSyncReady = false; const driveFilesCollection = new DriveFilesCollection(); const driveFoldersCollection = new DriveFoldersCollection(); @@ -60,16 +60,66 @@ export async function getUpdatedRemoteItems() { throw error; } } +export async function getUpdatedRemoteItemsByFolder(folderId: any) { + try { + const [allDriveFiles, allDriveFolders] = await Promise.all([ + driveFilesCollection.getAll(), + driveFoldersCollection.getAll(), + ]); + + if (!allDriveFiles.success) + throw new Error('Failed to retrieve all the drive files from local db'); + + if (!allDriveFolders.success) + throw new Error('Failed to retrieve all the drive folders from local db'); + return { + files: allDriveFiles.result, + folders: allDriveFolders.result, + }; + } catch (error) { + reportError(error as Error, { + description: + 'Something failed when updating the local db pulling the new changes from remote', + }); + throw error; + } +} ipcMain.handle('GET_UPDATED_REMOTE_ITEMS', async () => { Logger.debug('[MAIN] Getting updated remote items'); return getUpdatedRemoteItems(); }); -export function startRemoteSync(): Promise { - return remoteSyncManager.startRemoteSync(); +ipcMain.handle('GET_UPDATED_REMOTE_ITEMS_BY_FOLDER', async (folderId) => { + Logger.debug('[MAIN] Getting updated remote items'); + return getUpdatedRemoteItemsByFolder(folderId); +}); + +// export async function startRemoteSync(): Promise { +// await remoteSyncManager.startRemoteSync(); +// } +// export async function startRemoteProgresiveSync(): Promise { +export async function startRemoteSync(folderId?: number): Promise { + try { + const { files: _files, folders } = await remoteSyncManager.startRemoteSync( + folderId + ); + Logger.info('Remote sync started', folders?.length, 'folders'); + Logger.info('Remote sync started', _files?.length, 'files'); + + if (folderId && folders && folders.length > 0) { + await Promise.all( + folders.map(async (folder) => await startRemoteSync(folder.id)) + ); + } + Logger.info('Remote sync finished'); + return; + } catch (error) { + if (error instanceof Error) reportError(error); + } } ipcMain.handle('START_REMOTE_SYNC', async () => { + Logger.info('Received start remote sync event'); await startRemoteSync(); }); @@ -89,6 +139,7 @@ export async function updateRemoteSync(): Promise { // Wait before checking for updates, could be possible // that we received the notification, but if we check // for new data we don't receive it + Logger.info('Updating remote sync'); await sleep(2_000); await startRemoteSync(); updateSyncEngine(); @@ -98,12 +149,12 @@ export async function fallbackRemoteSync(): Promise { fallbackSyncEngine(); } -export function checkSyncEngineInProcess (milliSeconds: number) { +export function checkSyncEngineInProcess(milliSeconds: number) { const syncingStatus: RemoteSyncStatus = 'SYNCING'; const isSyncing = remoteSyncManager.getSyncStatus() === syncingStatus; const recentlySyncing = remoteSyncManager.recentlyWasSyncing(milliSeconds); return isSyncing || recentlySyncing; // syncing or recently was syncing -}; +} export function setIsProcessing(isProcessing: boolean) { remoteSyncManager.isProcessRunning = isProcessing; @@ -148,10 +199,18 @@ eventBus.on('RECEIVED_REMOTE_CHANGES', async () => { eventBus.on('USER_LOGGED_IN', async () => { Logger.info('Received user logged in event'); - await remoteSyncManager.startRemoteSync().catch((error) => { + // await remoteSyncManager.startRemoteSync().catch((error) => { + // Logger.error('Error starting remote sync manager', error); + // reportError(error); + // }); + try { + const userData = configStore.get('userData'); + await startRemoteSync(); + // traverse de esto + } catch (error) { Logger.error('Error starting remote sync manager', error); - reportError(error); - }); + if (error instanceof Error) reportError(error); + } }); eventBus.on('USER_LOGGED_OUT', () => { @@ -180,4 +239,3 @@ export async function checkSyncInProgress(milliSeconds: number) { ipcMain.handle('CHECK_SYNC_IN_PROGRESS', async (_, milliSeconds: number) => { return await checkSyncInProgress(milliSeconds); }); - diff --git a/src/apps/main/usage/service.ts b/src/apps/main/usage/service.ts index 71ec87e5c..4fd6a2fef 100644 --- a/src/apps/main/usage/service.ts +++ b/src/apps/main/usage/service.ts @@ -28,7 +28,7 @@ export class UserUsageService { } async calculateUsage(): Promise { - const [driveUsage, photosUsage, limitInBytes] = await Promise.all([ + const [driveUsage, photosUsage, limitInBytes] = await Promise.all([ this.getDriveUsage(), this.getPhotosUsage(), this.getLimit(), diff --git a/src/apps/shared/IPC/events/sync-engine.ts b/src/apps/shared/IPC/events/sync-engine.ts index 0a3dbd60e..cd555f97c 100644 --- a/src/apps/shared/IPC/events/sync-engine.ts +++ b/src/apps/shared/IPC/events/sync-engine.ts @@ -123,6 +123,10 @@ export type SyncEngineInvocableFunctions = { files: DriveFile[]; folders: DriveFolder[]; }>; + GET_UPDATED_REMOTE_ITEMS_BY_FOLDER: (folderId: number) => Promise<{ + files: DriveFile[]; + folders: DriveFolder[]; + }>; START_REMOTE_SYNC: () => Promise; }; diff --git a/src/apps/sync-engine/BindingManager.ts b/src/apps/sync-engine/BindingManager.ts index 550051262..6362c8ed1 100644 --- a/src/apps/sync-engine/BindingManager.ts +++ b/src/apps/sync-engine/BindingManager.ts @@ -34,7 +34,6 @@ export class BindingsManager { private progressBuffer = 0; private controllers: IControllers; private processingResolve?: (unknown?: unknown) => void; - private processingReject?: (unknown?: unknown) => void; constructor( private readonly container: DependencyContainer, @@ -300,9 +299,9 @@ export class BindingsManager { Logger.debug('[Handle Hydrate Callback] Preparing begins', task.path); // Crear una promesa que será resuelta por fetchDataCallback - const processingPromise = new Promise((resolve, reject) => { + const processingPromise = new Promise((resolve, _reject) => { this.processingResolve = resolve; - this.processingReject = reject; + // this.processingReject = reject; }); await this.container.virtualDrive.hydrateFile(task.path); diff --git a/src/apps/utils/date.ts b/src/apps/utils/date.ts index 8a107e75f..c9f36654e 100644 --- a/src/apps/utils/date.ts +++ b/src/apps/utils/date.ts @@ -5,3 +5,35 @@ export function getDateFromSeconds(seconds: number): Date { export function getSecondsFromDateString(dateString: string): number { return Math.trunc(new Date(dateString).valueOf() / 1000); } + +export function convertUTCToSpain(dateString: string) { + const date = new Date(dateString); // Crear un objeto Date a partir del string UTC + + // Opciones de formato para España (CET/CEST) + const options: Intl.DateTimeFormatOptions = { + timeZone: 'Europe/Madrid', + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + fractionalSecondDigits: 3, // Incluye milisegundos + hour12: false, // Formato de 24 horas + }; + + // Formatear la fecha a la franja horaria de España + const formatter = new Intl.DateTimeFormat('es-ES', options); + const parts = formatter.formatToParts(date); + + // Construir la fecha en el formato deseado + const formattedDate = `${ + parts.find((part) => part?.type === 'year')?.value + }-${parts.find((part) => part.type === 'month')?.value}-${ + parts.find((part) => part.type === 'day')?.value + }T${parts.find((part) => part.type === 'hour')?.value}:${ + parts.find((part) => part.type === 'minute')?.value + }:${parts.find((part) => part.type === 'second')?.value}.000Z`; + + return formattedDate; +} diff --git a/src/context/virtual-drive/items/application/RemoteItemsGenerator.ts b/src/context/virtual-drive/items/application/RemoteItemsGenerator.ts index e4f2510e6..249410fb1 100644 --- a/src/context/virtual-drive/items/application/RemoteItemsGenerator.ts +++ b/src/context/virtual-drive/items/application/RemoteItemsGenerator.ts @@ -54,4 +54,51 @@ export class RemoteItemsGenerator { return { files, folders }; } + + async getFolderItems( + folderId: number + ): Promise<{ files: ServerFile[]; folders: ServerFolder[] }> { + const updatedRemoteItems = await this.ipc.invoke( + 'GET_UPDATED_REMOTE_ITEMS_BY_FOLDER', + folderId + ); + + const files = updatedRemoteItems.files.map((updatedFile) => { + return { + bucket: updatedFile.bucket, + createdAt: updatedFile.createdAt, + encrypt_version: '03-aes', + fileId: updatedFile.fileId, + folderId: updatedFile.folderId, + id: updatedFile.id, + modificationTime: updatedFile.modificationTime, + name: updatedFile.name, + plainName: updatedFile.plainName, + size: updatedFile.size, + type: updatedFile.type ?? null, + updatedAt: updatedFile.updatedAt, + userId: updatedFile.userId, + status: updatedFile.status as ServerFileStatus, + uuid: updatedFile.uuid, + }; + }); + + const folders = updatedRemoteItems.folders.map( + (updatedFolder) => { + return { + bucket: updatedFolder.bucket ?? null, + createdAt: updatedFolder.createdAt, + id: updatedFolder.id, + name: updatedFolder.name, + parentId: updatedFolder.parentId ?? null, + updatedAt: updatedFolder.updatedAt, + plain_name: updatedFolder.plainName ?? null, + status: updatedFolder.status as ServerFolderStatus, + uuid: updatedFolder.uuid, + }; + } + ); + + return { files, folders }; + } } diff --git a/src/context/virtual-drive/items/application/TreeProgresiveBuilder.ts b/src/context/virtual-drive/items/application/TreeProgresiveBuilder.ts new file mode 100644 index 000000000..7cbae72d3 --- /dev/null +++ b/src/context/virtual-drive/items/application/TreeProgresiveBuilder.ts @@ -0,0 +1,32 @@ +import { ServerFileStatus } from '../../../shared/domain/ServerFile'; +import { ServerFolderStatus } from '../../../shared/domain/ServerFolder'; +import { Tree } from '../domain/Tree'; +import { RemoteItemsGenerator } from './RemoteItemsGenerator'; +import { Traverser } from './Traverser'; + +export class TreeProgresiveBuilder { + constructor( + private readonly remoteItemsGenerator: RemoteItemsGenerator, + private readonly traverser: Traverser + ) {} + + public setFilterStatusesToFilter(statuses: Array): void { + this.traverser.setFileStatusesToFilter(statuses); + } + + public setFolderStatusesToFilter(statuses: Array): void { + this.traverser.setFolderStatusesToFilter(statuses); + } + // hacer un recorrido progresivo empezar por el root y luego ir a los folders hijos y ejecutar el traverser + + async run(): Promise { + // obtener los items del root + + // ejecutar el traverser + + // ejecutar el recursiveTraverse para cada folder hijo + const items = await this.remoteItemsGenerator.getAll(); + + return this.traverser.run(items); + } +}