files - provide access to stat object from not modified error

This commit is contained in:
Benjamin Pasero 2021-05-28 10:19:04 +02:00
parent d5c73cc952
commit 466dd4e490
No known key found for this signature in database
GPG key ID: E6380CC4C8219E65
4 changed files with 28 additions and 9 deletions

View file

@ -6,7 +6,7 @@
import { localize } from 'vs/nls';
import { mark } from 'vs/base/common/performance';
import { Disposable, IDisposable, toDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED, hasFileReadStreamCapability, IFileSystemProviderWithFileReadStreamCapability, ensureFileSystemProviderError, IFileSystemProviderCapabilitiesChangeEvent, IReadFileStreamOptions, FileDeleteOptions, FilePermission } from 'vs/platform/files/common/files';
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED, hasFileReadStreamCapability, IFileSystemProviderWithFileReadStreamCapability, ensureFileSystemProviderError, IFileSystemProviderCapabilitiesChangeEvent, IReadFileStreamOptions, FileDeleteOptions, FilePermission, NotModifiedSinceFileOperationError } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { Emitter } from 'vs/base/common/event';
import { IExtUri, extUri, extUriIgnorePathCase, isAbsolutePath } from 'vs/base/common/resources';
@ -530,7 +530,14 @@ export class FileService extends Disposable implements IFileService {
await consumeStream(fileStream);
}
throw new FileOperationError(localize('err.read', "Unable to read file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options);
// Re-throw errors as file operation errors but preserve
// specific errors (such as not modified since)
const message = localize('err.read', "Unable to read file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString());
if (error instanceof NotModifiedSinceFileOperationError) {
throw new NotModifiedSinceFileOperationError(message, error.stat, options);
} else {
throw new FileOperationError(message, toFileOperationResult(error), options);
}
}
}
@ -598,7 +605,7 @@ export class FileService extends Disposable implements IFileService {
// Throw if file not modified since (unless disabled)
if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && options.etag === stat.etag) {
throw new FileOperationError(localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
throw new NotModifiedSinceFileOperationError(localize('fileNotModifiedError', "File not modified since"), stat, options);
}
// Throw if file is too large to load

View file

@ -11,7 +11,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { Event } from 'vs/base/common/event';
import { startsWithIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isNumber, isUndefinedOrNull } from 'vs/base/common/types';
import { isNumber } from 'vs/base/common/types';
import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer';
import { ReadableStreamEvents } from 'vs/base/common/stream';
import { CancellationToken } from 'vs/base/common/cancellation';
@ -1038,12 +1038,23 @@ export interface ICreateFileOptions {
}
export class FileOperationError extends Error {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IReadFileOptions & IWriteFileOptions & ICreateFileOptions) {
constructor(
message: string,
readonly fileOperationResult: FileOperationResult,
readonly options?: IReadFileOptions & IWriteFileOptions & ICreateFileOptions
) {
super(message);
}
}
static isFileOperationError(obj: unknown): obj is FileOperationError {
return obj instanceof Error && !isUndefinedOrNull((obj as FileOperationError).fileOperationResult);
export class NotModifiedSinceFileOperationError extends FileOperationError {
constructor(
message: string,
readonly stat: IFileStatWithMetadata,
options?: IReadFileOptions
) {
super(message, FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
}
}

View file

@ -13,7 +13,7 @@ import { join, basename, dirname, posix } from 'vs/base/common/path';
import { copy, Promises, rimraf, rimrafSync } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream } from 'fs';
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata, IReadFileOptions, FilePermission } from 'vs/platform/files/common/files';
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata, IReadFileOptions, FilePermission, NotModifiedSinceFileOperationError } from 'vs/platform/files/common/files';
import { NullLogService } from 'vs/platform/log/common/log';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { DisposableStore } from 'vs/base/common/lifecycle';
@ -1491,6 +1491,7 @@ flakySuite('Disk File Service', function () {
assert.ok(error);
assert.strictEqual(error!.fileOperationResult, FileOperationResult.FILE_NOT_MODIFIED_SINCE);
assert.ok(error instanceof NotModifiedSinceFileOperationError && error.stat);
assert.strictEqual(fileProvider.totalBytesRead, 0);
}

View file

@ -851,8 +851,8 @@ export class TestFileService implements IFileService {
fireFileChanges(event: FileChangesEvent): void { this._onDidFilesChange.fire(event); }
get onDidRunOperation(): Event<FileOperationEvent> { return this._onDidRunOperation.event; }
fireAfterOperation(event: FileOperationEvent): void { this._onDidRunOperation.fire(event); }
resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>;
resolve(resource: URI, _options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>;
resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat> {
return Promise.resolve({
resource,