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

[PB-1029] fix: folder creation with name #387

Merged
merged 16 commits into from
Oct 5, 2023
Merged
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
}
},
"jest": {
"setupFilesAfterEnv": [
"jest-extended/all"
],
"globals": {
"ts-jest": {
"tsconfig": "tsconfig.test.json"
Expand Down
43 changes: 0 additions & 43 deletions src/test/main/thumbnails/extract-pdf-page.test.ts

This file was deleted.

20 changes: 12 additions & 8 deletions src/workers/sync-engine/BindingManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Logger from 'electron-log';
import { DependencyContainer } from './dependency-injection/DependencyContainer';
import { buildControllers } from './callbacks-controllers/buildControllers';
import { executeControllerWithFallback } from './callbacks-controllers/middlewares/executeControllerWithFallback';
import { FilePlaceholderId } from './modules/placeholders/domain/FilePlaceholderId';

export class BindingsManager {
private static readonly PROVIDER_NAME = 'Internxt';
Expand All @@ -26,11 +28,9 @@ export class BindingsManager {
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 @@ -43,11 +43,15 @@ export class BindingsManager {
contentsId: string,
callback: (response: boolean) => void
) => {
controllers.renameOrMoveFile.execute(
absolutePath,
contentsId,
callback
);
const fn = executeControllerWithFallback({
handler: controllers.renameOrMove.execute.bind(
controllers.renameOrMove
),
fallback: controllers.offline.renameOrMove.execute.bind(
controllers.offline.renameOrMove
),
});
fn(absolutePath, contentsId, callback);
},
notifyFileAddedCallback: (
absolutePath: string,
Expand All @@ -56,7 +60,7 @@ export class BindingsManager {
controllers.addFile.execute(absolutePath, callback);
},
fetchDataCallback: (
contentsId: string,
contentsId: FilePlaceholderId,
callback: (success: boolean, path: string) => void
) => {
controllers.downloadFile
Expand Down
19 changes: 15 additions & 4 deletions src/workers/sync-engine/callbacks-controllers/buildControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import { AddController } from './controllers/AddController';
import { DeleteController } from './controllers/DeleteController';
import { DownloadFileController } from './controllers/DownloadFileController';
import { RenameOrMoveController } from './controllers/RenameOrMoveController';
import { OfflineRenameOrMoveController } from './controllers/offline/OfflineRenameOrMoveController';

export function buildControllers(container: DependencyContainer) {
const addFileController = new AddController(
container.absolutePathToRelativeConverter,
container.fileCreationOrchestrator,
container.folderCreator
container.folderCreator,
container.offline.folderCreator
);

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

const renameOrMoveFileController = new RenameOrMoveController(
container.filePathFromAbsolutePathCreator,
const renameOrMoveController = new RenameOrMoveController(
container.absolutePathToRelativeConverter,
container.filePathUpdater,
container.folderPathUpdater,
deleteController
Expand All @@ -28,10 +31,18 @@ export function buildControllers(container: DependencyContainer) {
container.localRepositoryRefresher
);

const offlineRenameOrMoveController = new OfflineRenameOrMoveController(
container.absolutePathToRelativeConverter,
container.offline.folderPathUpdater
);

return {
addFile: addFileController,
renameOrMoveFile: renameOrMoveFileController,
renameOrMove: renameOrMoveController,
delete: deleteController,
downloadFile: downloadFileController,
offline: {
renameOrMove: offlineRenameOrMoveController,
},
} as const;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,36 @@ import Logger from 'electron-log';
import { CallbackController } from './CallbackController';
import { rawPathIsFolder } from '../helpers/rawPathIsFolder';
import { FolderCreator } from '../../modules/folders/application/FolderCreator';
import { MapObserver } from 'workers/sync-engine/modules/shared/domain/MapObserver';
import { FileCreationOrchestrator } from 'workers/sync-engine/modules/boundaryBridge/application/FileCreationOrchestrator';
import { MapObserver } from '../../modules/shared/domain/MapObserver';
import { FileCreationOrchestrator } from '../../modules/boundaryBridge/application/FileCreationOrchestrator';
import { OfflineFolderCreator } from '../../modules/folders/application/Offline/OfflineFolderCreator';
import { OfflineFolder } from '../../modules/folders/domain/OfflineFolder';
import { createFilePlaceholderId } from '../../modules/placeholders/domain/FilePlaceholderId';
import { createFolderPlaceholderId } from '../../modules/placeholders/domain/FolderPlaceholderId';
import { PlatformPathConverter } from '../../modules/shared/application/PlatformPathConverter';
import { AbsolutePathToRelativeConverter } from '../../modules/shared/application/AbsolutePathToRelativeConverter';

type Queue = Map<string, (acknowledge: boolean, id: string) => void>;
type FileCreationQueue = Map<
string,
(acknowledge: boolean, id: string) => void
>;
type FolderCreationQueue = Map<
OfflineFolder,
(acknowledge: boolean, id: string) => void
>;
type CreationCallback = (acknowledge: boolean, id: string) => void;

export class AddController extends CallbackController {
private readonly filesQueue: Queue;
private readonly foldersQueue: Queue;
private readonly filesQueue: FileCreationQueue;
private readonly foldersQueue: FolderCreationQueue;

private readonly observer: MapObserver;

constructor(
private readonly absolutePathToRelativeConverter: AbsolutePathToRelativeConverter,
private readonly fileCreationOrchestrator: FileCreationOrchestrator,
private readonly folderCreator: FolderCreator
private readonly folderCreator: FolderCreator,
private readonly offlineFolderCreator: OfflineFolderCreator
) {
super();

Expand All @@ -27,62 +43,93 @@ export class AddController extends CallbackController {
}

private createFile = async (
absolutePath: string,
posixRelativePath: string,
callback: (acknowledge: boolean, id: string) => void
) => {
try {
const contentsId = await this.fileCreationOrchestrator.run(absolutePath);
return callback(true, contentsId);
const contentsId = await this.fileCreationOrchestrator.run(
posixRelativePath
);
return callback(true, createFilePlaceholderId(contentsId));
} catch (error: unknown) {
Logger.error('Error when adding a file: ', error);
callback(false, '');
} finally {
this.filesQueue.delete(absolutePath);
this.filesQueue.delete(posixRelativePath);
}
};

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

private createFolders = async () => {
for (const [absolutePath, callback] of this.foldersQueue) {
await this.createFolder(absolutePath, callback);
Logger.debug('FOLDERS TO CREATE', this.foldersQueue.size);
for (const [offlineFolder, callback] of this.foldersQueue) {
await this.createFolder(offlineFolder, callback);
}
};

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

private enqueueFolder = (
posixRelativePath: string,
callback: CreationCallback
) => {
try {
const offlineFolder = this.offlineFolderCreator.run(posixRelativePath);
callback(true, createFolderPlaceholderId(offlineFolder.uuid));
this.foldersQueue.set(offlineFolder, () => {
//no-op
});
} catch (error: unknown) {
Logger.error('Error on folder creation: ', error);
callback(false, '');
}
};

async execute(
absolutePath: string,
callback: (acknowledge: boolean, id: string) => void
callback: CreationCallback
): Promise<void> {
const win32RelativePath =
this.absolutePathToRelativeConverter.run(absolutePath);

const posixRelativePath =
PlatformPathConverter.winToPosix(win32RelativePath);

if (rawPathIsFolder(absolutePath)) {
this.foldersQueue.set(absolutePath, callback);
this.enqueueFolder(posixRelativePath, callback);
await this.createFolders();
await this.createFiles();
return;
}

Logger.debug('File is going to be queued: ', absolutePath);
this.filesQueue.set(absolutePath, callback);
this.filesQueue.set(posixRelativePath, callback);

if (this.foldersQueue.size === 0) {
this.createFiles();
Logger.debug(
'File is not going to be queued. Creating...',
posixRelativePath
);
await this.createFiles();
} else {
Logger.debug('File has been queued: ', posixRelativePath);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
import {
FilePlaceholderId,
isFilePlaceholderId,
} from '../../modules/placeholders/domain/FilePlaceholderId';
import {
FolderPlaceholderId,
isFolderPlaceholderId,
} from '../../modules/placeholders/domain/FolderPlaceholderId';

export abstract class CallbackController {
protected trim(id: string): string {
return id.replace(
Expand All @@ -6,12 +15,20 @@ export abstract class CallbackController {
''
);
}
protected isContentsId(id: string): boolean {

protected isFilePlaceholder(id: string): id is FilePlaceholderId {
// make sure the id is trimmed before comparing
// if it was already trimmed should not change its length
const trimmed = this.trim(id);

return isFilePlaceholderId(trimmed);
}

protected isFolderPlaceholder(id: string): id is FolderPlaceholderId {
// 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;
return isFolderPlaceholderId(trimmed);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,18 @@ export class DeleteController extends CallbackController {
async execute(contentsId: string) {
const trimmedId = this.trim(contentsId);

if (this.isContentsId(trimmedId)) {
this.filesQueue.push(trimmedId);
if (this.isFilePlaceholder(trimmedId)) {
const [_, contentsId] = trimmedId.split(':');
this.filesQueue.push(contentsId);
return;
}

this.foldersQueue.push(trimmedId);
if (this.isFolderPlaceholder(trimmedId)) {
const [_, folderUuid] = trimmedId.split(':');
this.foldersQueue.push(folderUuid);
return;
}

throw new Error(`Placeholder Id not identified: ${trimmedId}`);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { FilePlaceholderId } from 'workers/sync-engine/modules/placeholders/domain/FilePlaceholderId';
import { ContentsDownloader } from '../../modules/contents/application/ContentsDownloader';
import { FileFinderByContentsId } from '../../modules/files/application/FileFinderByContentsId';
import { LocalRepositoryRepositoryRefresher } from '../../modules/files/application/LocalRepositoryRepositoryRefresher';
import { CallbackController } from './CallbackController';
import Logger from 'electron-log';

export class DownloadFileController extends CallbackController {
constructor(
Expand All @@ -18,12 +20,17 @@ export class DownloadFileController extends CallbackController {
return await this.downloader.run(file);
}

async execute(contentsId: string): Promise<string> {
async execute(contentsId: FilePlaceholderId): Promise<string> {
const trimmedId = this.trim(contentsId);

try {
return await this.action(trimmedId);
} catch {
const [_, contentsId] = trimmedId.split(':');
return await this.action(contentsId);
} catch (error: unknown) {
Logger.error(
'Error downloading a file, going to refresh and retry: ',
error
);
await this.localRepositoryRefresher.run();

return await new Promise((resolve, reject) => {
Expand Down
Loading
Loading