Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[_] feat: folders placeholders #383

Merged
merged 18 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 25 additions & 19 deletions src/workers/sync-engine/BindingManager.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import { VirtualDrive } from 'virtual-drive';
import Logger from 'electron-log';
import { Folder } from './modules/folders/domain/Folder';
import { File } from './modules/files/domain/File';
import { DependencyContainer } from './dependency-injection/DependencyContainer';
import { buildControllers } from './callbacks-controllers/buildControllers';

export class BindingsManager {
private static readonly PROVIDER_NAME = 'Internxt';

constructor(
private readonly drive: VirtualDrive,
private readonly controllers: ReturnType<typeof buildControllers>,
private readonly container: DependencyContainer,
private readonly paths: {
root: string;
icon: string;
}
) {}

private createFolderPlaceholder(folder: Folder) {
public createFolderPlaceholder(folder: Folder) {
// In order to create a folder placeholder it's path must en with /
const folderPath = `${folder.path.value}/`;

this.drive.createItemByPath(folderPath, folder.uuid);
this.container.virtualDrive.createItemByPath(folderPath, folder.uuid);
}
public createFilePlaceholder(file: File) {
this.container.virtualDrive.createItemByPath(
file.path.value,
file.contentsId,
file.size
);
}

public createPlaceHolders(items: Array<File | Folder>) {
items.forEach((item) => {
if (item.isFile()) {
this.drive.createItemByPath(
item.path.value,
item.contentsId,
item.size
);
return;
return this.createFilePlaceholder(item);
}

this.createFolderPlaceholder(item);
Expand All @@ -40,17 +41,22 @@ export class BindingsManager {

async start(version: string, providerId: string) {
await this.stop();

const controllers = buildControllers(this.container);

const callbacks = {
notifyDeleteCallback: (
contentsId: string,
callback: (response: boolean) => void
) => {
this.controllers.deleteFile
controllers.delete
.execute(contentsId)
.then(() => {
Logger.debug('DELETE RESPONSE SUCCESSFUL');
callback(true);
})
.catch((error: Error) => {
Logger.debug('DELETE RESPONSE NOT SUCCESSFUL');
Logger.error(error);
callback(false);
});
Expand All @@ -63,7 +69,7 @@ export class BindingsManager {
contentsId: string,
callback: (response: boolean) => void
) => {
this.controllers.renameOrMoveFile.execute(
controllers.renameOrMoveFile.execute(
absolutePath,
contentsId,
callback
Expand All @@ -73,13 +79,13 @@ export class BindingsManager {
absolutePath: string,
callback: (acknowledge: boolean, id: string) => void
) => {
this.controllers.addFile.execute(absolutePath, callback);
controllers.addFile.execute(absolutePath, callback);
},
fetchDataCallback: (
contentsId: string,
callback: (success: boolean, path: string) => void
) => {
this.controllers.downloadFile
controllers.downloadFile
.execute(contentsId)
.then((path: string) => {
callback(true, path);
Expand Down Expand Up @@ -121,22 +127,22 @@ export class BindingsManager {
},
};

await this.drive.registerSyncRoot(
await this.container.virtualDrive.registerSyncRoot(
BindingsManager.PROVIDER_NAME,
version,
providerId,
callbacks,
this.paths.icon
);

await this.drive.connectSyncRoot();
await this.container.virtualDrive.connectSyncRoot();
}

watch() {
this.drive.watchAndWait(this.paths.root);
this.container.virtualDrive.watchAndWait(this.paths.root);
}

async stop() {
await this.drive.disconnectSyncRoot();
await this.container.virtualDrive.disconnectSyncRoot();
}
}
18 changes: 12 additions & 6 deletions src/workers/sync-engine/callbacks-controllers/buildControllers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DependencyContainer } from '../dependency-injection/DependencyContainer';
import { AddFileController } from './controllers/AddFileController';
import { DeleteFileController } from './controllers/DeleteFileController';
import { DeleteController } from './controllers/DeleteController';
import { DownloadFileController } from './controllers/DownloadFileController';
import { RenameOrMoveController } from './controllers/RenameOrMoveController';

Expand All @@ -10,17 +10,23 @@ export function buildControllers(container: DependencyContainer) {
container.filePathFromAbsolutePathCreator,
container.fileCreator,
container.fileDeleter,
container.fileByPartialSearcher
container.fileByPartialSearcher,
container.folderCreator,
container.folderFinder
);

const deleteController = new DeleteController(
container.fileDeleter,
container.folderDeleter
);

const renameOrMoveFileController = new RenameOrMoveController(
container.filePathFromAbsolutePathCreator,
container.filePathUpdater,
container.fileDeleter
container.folderPathUpdater,
deleteController
);

const deleteFileController = new DeleteFileController(container.fileDeleter);

const downloadFileController = new DownloadFileController(
container.fileFinderByContentsId,
container.contentsDownloader,
Expand All @@ -30,7 +36,7 @@ export function buildControllers(container: DependencyContainer) {
return {
addFile: addFileController,
renameOrMoveFile: renameOrMoveFileController,
deleteFile: deleteFileController,
delete: deleteController,
downloadFile: downloadFileController,
} as const;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,49 @@ import { RetryContentsUploader } from '../../modules/contents/application/RetryC
import { FileDeleter } from '../../modules/files/application/FileDeleter';
import { FileByPartialSearcher } from '../../modules/files/application/FileByPartialSearcher';
import { PlatformPathConverter } from '../../modules/shared/test/helpers/PlatformPathConverter';
import { rawPathIsFolder } from '../helpers/rawPathIsFolder';
import { FolderCreator } from '../../modules/folders/application/FolderCreator';
import { MapObserver } from 'workers/sync-engine/modules/shared/domain/MapObserver';
import { FolderFinder } from 'workers/sync-engine/modules/folders/application/FolderFinder';

export type DehydrateAndCreatePlaceholder = (
id: string,
relativePath: string,
size: number
) => void;
type Queue = Map<string, (acknowledge: boolean, id: string) => void>;

export class AddFileController extends CallbackController {
private readonly filesQueue: Queue;
private readonly foldersQueue: Queue;

private readonly observer: MapObserver;

constructor(
private readonly contentsUploader: RetryContentsUploader,
private readonly filePathFromAbsolutePathCreator: FilePathFromAbsolutePathCreator,
private readonly fileCreator: FileCreator,
private readonly fileDeleter: FileDeleter,
private readonly searchByPartial: FileByPartialSearcher
private readonly searchByPartial: FileByPartialSearcher,
private readonly folderCreator: FolderCreator,
private readonly folderFinder: FolderFinder
) {
super();

this.filesQueue = new Map();
this.foldersQueue = new Map();

this.observer = new MapObserver(this.foldersQueue, this.createFiles);
this.observer.startObserving();
}

async execute(
private createFile = async (
absolutePath: string,
callback: (acknowledge: boolean, id: string) => void
): Promise<void> {
) => {
try {
const path = this.filePathFromAbsolutePathCreator.run(absolutePath);
const file = this.searchByPartial.run({
path: PlatformPathConverter.winToPosix(path.value),
});

this.folderFinder.findFromFilePath(path);

const fileContents = await this.contentsUploader.run(absolutePath);

if (file) {
Expand All @@ -51,6 +66,53 @@ export class AddFileController extends CallbackController {
} catch (error: unknown) {
Logger.error('Error when adding a file: ', error);
callback(false, '');
} finally {
this.filesQueue.delete(absolutePath);
}
};

private createFiles = async () => {
for (const [absolutePath, callback] of this.filesQueue) {
await this.createFile(absolutePath, callback);
}
};

private createFolder = async (
absolutePath: string,
callback: (acknowledge: boolean, id: string) => void
) => {
Logger.info('Creating folder', absolutePath);
try {
const folder = await this.folderCreator.run(absolutePath);
callback(true, folder.uuid);
} catch (error: unknown) {
Logger.error('Error creating a folder: ', error);
callback(false, '');
} finally {
this.foldersQueue.delete(absolutePath);
}
};

private createFolders = async () => {
for (const [absolutePath, callback] of this.foldersQueue) {
await this.createFolder(absolutePath, callback);
}
};

async execute(
absolutePath: string,
callback: (acknowledge: boolean, id: string) => void
): Promise<void> {
if (rawPathIsFolder(absolutePath)) {
this.foldersQueue.set(absolutePath, callback);
await this.createFolders();
return;
}

Logger.debug('File is going to be queued: ', absolutePath);
this.filesQueue.set(absolutePath, callback);
if (this.foldersQueue.size === 0) {
this.createFiles();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,12 @@ export abstract class CallbackController {
''
);
}
protected isContentsId(id: string): boolean {
// make sure the id is trimmed before comparing
// if it was already trimmed should not change its length
const trimmed = this.trim(id);

// TODO: need a better way to detect if its a file or a folder
return trimmed.length === 24;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FolderDeleter } from 'workers/sync-engine/modules/folders/application/FolderDeleter';
import { FileDeleter } from '../../modules/files/application/FileDeleter';
import { CallbackController } from './CallbackController';
import { DelayQueue } from 'workers/sync-engine/modules/shared/domain/DelayQueue';

export class DeleteController extends CallbackController {
private readonly filesQueue: DelayQueue;
private readonly foldersQueue: DelayQueue;

constructor(
private readonly fileDeleter: FileDeleter,
private readonly folderDeleter: FolderDeleter
) {
super();

const deleteFile = async (file: string) => {
await this.fileDeleter.run(file);
};

const deleteFolder = async (folder: string) => {
await this.folderDeleter.run(folder);
};

const canDeleteFolders = () => {
// Folders can always be deleted
return true;
};

this.foldersQueue = new DelayQueue(
'folders',
deleteFolder,
canDeleteFolders
);

const canDeleteFiles = () => {
// Files cannot be deleted if there are folders on the queue
return this.foldersQueue.size === 0;
};

this.filesQueue = new DelayQueue('files', deleteFile, canDeleteFiles);
}

async execute(contentsId: string) {
const trimmedId = this.trim(contentsId);

if (this.isContentsId(trimmedId)) {
this.filesQueue.push(trimmedId);
return;
}

this.foldersQueue.push(trimmedId);
}
}

This file was deleted.

Loading