fix: nested cloud sketch folder sync

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2024-02-21 12:32:41 +01:00
parent aa9b10d68e
commit 478c36c5bd
2 changed files with 57 additions and 37 deletions

View File

@ -1,35 +1,36 @@
import { inject, injectable } from '@theia/core/shared/inversify'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import URI from '@theia/core/lib/common/uri';
import { Event } from '@theia/core/lib/common/event';
import { import {
Disposable, Disposable,
DisposableCollection, DisposableCollection,
} from '@theia/core/lib/common/disposable'; } from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { Event } from '@theia/core/lib/common/event';
import { import URI from '@theia/core/lib/common/uri';
Stat, import { inject, injectable } from '@theia/core/shared/inversify';
FileType,
FileChange,
FileWriteOptions,
FileDeleteOptions,
FileOverwriteOptions,
FileSystemProvider,
FileSystemProviderError,
FileSystemProviderErrorCode,
FileSystemProviderCapabilities,
WatchOptions,
} from '@theia/filesystem/lib/common/files';
import { import {
FileService, FileService,
FileServiceContribution, FileServiceContribution,
} from '@theia/filesystem/lib/browser/file-service'; } from '@theia/filesystem/lib/browser/file-service';
import {
FileChange,
FileDeleteOptions,
FileOverwriteOptions,
FileSystemProvider,
FileSystemProviderCapabilities,
FileSystemProviderError,
FileSystemProviderErrorCode,
FileType,
FileWriteOptions,
Stat,
WatchOptions,
createFileSystemProviderError,
} from '@theia/filesystem/lib/common/files';
import { SketchesService } from '../../common/protocol';
import { stringToUint8Array } from '../../common/utils';
import { ArduinoPreferences } from '../arduino-preferences';
import { AuthenticationClientService } from '../auth/authentication-client-service'; import { AuthenticationClientService } from '../auth/authentication-client-service';
import { CreateApi } from './create-api'; import { CreateApi } from './create-api';
import { CreateUri } from './create-uri'; import { CreateUri } from './create-uri';
import { SketchesService } from '../../common/protocol'; import { Create, isNotFound } from './typings';
import { ArduinoPreferences } from '../arduino-preferences';
import { Create } from './typings';
import { stringToUint8Array } from '../../common/utils';
@injectable() @injectable()
export class CreateFsProvider export class CreateFsProvider
@ -90,6 +91,7 @@ export class CreateFsProvider
size: 0, size: 0,
}; };
} }
try {
const resource = await this.getCreateApi.stat(uri.path.toString()); const resource = await this.getCreateApi.stat(uri.path.toString());
const mtime = Date.parse(resource.modified_at); const mtime = Date.parse(resource.modified_at);
return { return {
@ -98,6 +100,18 @@ export class CreateFsProvider
mtime, mtime,
size: 0, size: 0,
}; };
} catch (err) {
let errToRethrow = err;
// Not Found (Create API) errors must be remapped to VS Code filesystem provider specific errors
// https://code.visualstudio.com/api/references/vscode-api#FileSystemError
if (isNotFound(err)) {
errToRethrow = createFileSystemProviderError(
err,
FileSystemProviderErrorCode.FileNotFound
);
}
throw errToRethrow;
}
} }
async mkdir(uri: URI): Promise<void> { async mkdir(uri: URI): Promise<void> {

View File

@ -389,15 +389,21 @@ export class CloudSketchbookTree extends SketchbookTree {
private async sync(source: URI, dest: URI): Promise<void> { private async sync(source: URI, dest: URI): Promise<void> {
const { filesToWrite, filesToDelete } = await this.treeDiff(source, dest); const { filesToWrite, filesToDelete } = await this.treeDiff(source, dest);
await Promise.all( // Sort by the URIs. The shortest comes first. It's to ensure creating the parent folder for nested resources, for example.
filesToWrite.map(async ({ source, dest }) => { // When sorting the URIs, it does not matter whether on source or dest, only the URI path and its length matters; they're the same for a source+dest pair
if ((await this.fileService.resolve(source)).isFile) { filesToWrite.sort(
const content = await this.fileService.read(source); (left, right) =>
return this.fileService.write(dest, content.value); left.source.path.toString().length - right.source.path.toString().length
}
return this.fileService.createFolder(dest);
})
); );
for (const { source, dest } of filesToWrite) {
const stat = await this.fileService.resolve(source);
if (stat.isFile) {
const content = await this.fileService.read(source);
await this.fileService.write(dest, content.value);
} else {
await this.fileService.createFolder(dest);
}
}
await Promise.all( await Promise.all(
filesToDelete.map((file) => filesToDelete.map((file) =>