Skip to content

Commit

Permalink
Better file system options checking and error messages
Browse files Browse the repository at this point in the history
Error messages now provide actionable feedback on FS configuration options that may have been mistyped or specified incorrectly.

Options objects are now optional for all `Create()` methods (of course, it will fail if the FS requires any options!).

Adds tests for the new error messages.

Fixes #183.
  • Loading branch information
John Vilk committed Aug 7, 2017
1 parent dcc337f commit 297844f
Show file tree
Hide file tree
Showing 25 changed files with 640 additions and 95 deletions.
5 changes: 4 additions & 1 deletion karma.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ let karmaFiles = [
// Main module and fixtures loader
'test/harness/test.js',
// WebWorker script.
{ pattern: 'test/harness/factories/workerfs_worker.js', included: false, watched: true }
{ pattern: 'test/harness/factories/workerfs_worker.js', included: false, watched: true },
// Source map support
{ pattern: 'src/**/*', included: false, watched: false },
{ pattern: 'test/**/*', included: false, watched: false }
];

// The presence of the Dropbox library dynamically toggles the tests.
Expand Down
19 changes: 16 additions & 3 deletions src/backend/AsyncMirror.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {FileSystem, SynchronousFileSystem, BFSOneArgCallback, BFSCallback} from '../core/file_system';
import {FileSystem, SynchronousFileSystem, BFSOneArgCallback, BFSCallback, FileSystemOptions} from '../core/file_system';
import {ApiError, ErrorCode} from '../core/api_error';
import {FileFlag} from '../core/file_flag';
import {File} from '../core/file';
Expand Down Expand Up @@ -87,6 +87,19 @@ export interface AsyncMirrorOptions {
* ```
*/
export default class AsyncMirror extends SynchronousFileSystem implements FileSystem {
public static readonly Name = "AsyncMirror";

public static readonly Options: FileSystemOptions = {
sync: {
type: "object",
description: "The synchronous file system to mirror the asynchronous file system to."
},
async: {
type: "object",
description: "The asynchronous file system to mirror."
}
};

/**
* Constructs and initializes an AsyncMirror file system with the given options.
*/
Expand Down Expand Up @@ -135,11 +148,11 @@ export default class AsyncMirror extends SynchronousFileSystem implements FileSy
if (!sync.supportsSynch()) {
throw new Error("The first argument to AsyncMirror needs to be a synchronous file system.");
}
deprecationMessage(deprecateMsg, "AsyncMirror", { sync: "sync file system instance", async: "async file system instance"});
deprecationMessage(deprecateMsg, AsyncMirror.Name, { sync: "sync file system instance", async: "async file system instance"});
}

public getName(): string {
return "AsyncMirror";
return AsyncMirror.Name;
}

public _syncSync(fd: PreloadFile<any>) {
Expand Down
22 changes: 19 additions & 3 deletions src/backend/Dropbox.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PreloadFile from '../generic/preload_file';
import {BaseFileSystem, FileSystem, BFSOneArgCallback, BFSCallback} from '../core/file_system';
import {BaseFileSystem, FileSystem, BFSOneArgCallback, BFSCallback, FileSystemOptions} from '../core/file_system';
import {FileFlag} from '../core/file_flag';
import {default as Stats, FileType} from '../core/node_fs_stats';
import {ApiError, ErrorCode} from '../core/api_error';
Expand Down Expand Up @@ -352,6 +352,22 @@ export interface DropboxFileSystemOptions {
* NOTE: You must use the v0.10 version of the [Dropbox JavaScript SDK](https://www.npmjs.com/package/dropbox).
*/
export default class DropboxFileSystem extends BaseFileSystem implements FileSystem {
public static readonly Name = "Dropbox";

public static readonly Options: FileSystemOptions = {
client: {
type: "object",
description: "An *authenticated* Dropbox client. Must be from the 0.10 JS SDK.",
validator: (opt: Dropbox.Client, cb: BFSOneArgCallback): void => {
if (opt.isAuthenticated && opt.isAuthenticated()) {
cb();
} else {
cb(new ApiError(ErrorCode.EINVAL, `'client' option must be an authenticated Dropbox client from the v0.10 JS SDK.`));
}
}
}
};

/**
* Creates a new DropboxFileSystem instance with the given options.
* Must be given an *authenticated* DropboxJS client from the old v0.10 version of the Dropbox JS SDK.
Expand All @@ -378,12 +394,12 @@ export default class DropboxFileSystem extends BaseFileSystem implements FileSys
constructor(client: Dropbox.Client, deprecateMsg = true) {
super();
this._client = new CachedDropboxClient(client);
deprecationMessage(deprecateMsg, "Dropbox", { client: "authenticated dropbox client instance" });
deprecationMessage(deprecateMsg, DropboxFileSystem.Name, { client: "authenticated dropbox client instance" });
constructErrorCodeLookup();
}

public getName(): string {
return 'Dropbox';
return DropboxFileSystem.Name;
}

public isReadOnly(): boolean {
Expand Down
11 changes: 10 additions & 1 deletion src/backend/Emscripten.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {SynchronousFileSystem, BFSOneArgCallback, BFSCallback, BFSThreeArgCallback} from '../core/file_system';
import {SynchronousFileSystem, BFSOneArgCallback, BFSCallback, BFSThreeArgCallback, FileSystemOptions} from '../core/file_system';
import {default as Stats, FileType} from '../core/node_fs_stats';
import {FileFlag} from '../core/file_flag';
import {BaseFile, File} from '../core/file';
Expand Down Expand Up @@ -192,6 +192,15 @@ export interface EmscriptenFileSystemOptions {
* Mounts an Emscripten file system into the BrowserFS file system.
*/
export default class EmscriptenFileSystem extends SynchronousFileSystem {
public static readonly Name = "EmscriptenFileSystem";

public static readonly Options: FileSystemOptions = {
FS: {
type: "object",
description: "The Emscripten file system to use (the `FS` variable)"
}
};

/**
* Create an EmscriptenFileSystem instance with the given options.
*/
Expand Down
17 changes: 14 additions & 3 deletions src/backend/FolderAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BaseFileSystem, FileSystem, BFSCallback} from '../core/file_system';
import {BaseFileSystem, FileSystem, BFSCallback, FileSystemOptions} from '../core/file_system';
import * as path from 'path';
import {ApiError} from '../core/api_error';

Expand Down Expand Up @@ -31,6 +31,19 @@ export interface FolderAdapterOptions {
* ```
*/
export default class FolderAdapter extends BaseFileSystem implements FileSystem {
public static readonly Name = "FolderAdapter";

public static readonly Options: FileSystemOptions = {
folder: {
type: "string",
description: "The folder to use as the root directory"
},
wrapped: {
type: "object",
description: "The file system to wrap"
}
};

/**
* Creates a FolderAdapter instance with the given options.
*/
Expand All @@ -46,8 +59,6 @@ export default class FolderAdapter extends BaseFileSystem implements FileSystem
/**
* Wraps a file system, and uses the given folder as its root.
*
*
*
* @param folder The folder to use as the root directory.
* @param wrapped The file system to wrap.
*/
Expand Down
34 changes: 25 additions & 9 deletions src/backend/HTML5FS.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PreloadFile from '../generic/preload_file';
import {BaseFileSystem, FileSystem as IFileSystem, BFSOneArgCallback, BFSCallback} from '../core/file_system';
import {BaseFileSystem, FileSystem as IFileSystem, BFSOneArgCallback, BFSCallback, FileSystemOptions} from '../core/file_system';
import {ApiError, ErrorCode} from '../core/api_error';
import {FileFlag, ActionType} from '../core/file_flag';
import {default as Stats, FileType} from '../core/node_fs_stats';
Expand Down Expand Up @@ -156,6 +156,21 @@ export interface HTML5FSOptions {
* only available in Chrome.
*/
export default class HTML5FS extends BaseFileSystem implements IFileSystem {
public static readonly Name = "HTML5FS";

public static readonly Options: FileSystemOptions = {
size: {
type: "number",
optional: true,
description: "Storage quota to request, in megabytes. Allocated value may be less. Defaults to 5."
},
type: {
type: "number",
optional: true,
description: "window.PERSISTENT or window.TEMPORARY. Defaults to PERSISTENT."
}
};

/**
* Creates an HTML5FS instance with the given options.
*/
Expand Down Expand Up @@ -188,11 +203,11 @@ export default class HTML5FS extends BaseFileSystem implements IFileSystem {
// Convert MB to bytes.
this.size = 1024 * 1024 * size;
this.type = type;
deprecationMessage(deprecateMsg, "HTML5FS", {size: size, type: type});
deprecationMessage(deprecateMsg, HTML5FS.Name, {size: size, type: type});
}

public getName(): string {
return 'HTML5 FileSystem';
return HTML5FS.Name;
}

public isReadOnly(): boolean {
Expand Down Expand Up @@ -436,14 +451,15 @@ export default class HTML5FS extends BaseFileSystem implements IFileSystem {
*/
public readdir(path: string, cb: BFSCallback<string[]>): void {
this._readdir(path, (e: ApiError, entries?: Entry[]): void => {
if (e) {
if (entries) {
const rv: string[] = [];
for (const entry of entries) {
rv.push(entry.name);
}
cb(null, rv);
} else {
return cb(e);
}
const rv: string[] = [];
for (let i = 0; i < entries!.length; i++) {
rv.push(entries![i].name);
}
cb(null, rv);
});
}

Expand Down
15 changes: 8 additions & 7 deletions src/backend/InMemory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BFSCallback} from '../core/file_system';
import {BFSCallback, FileSystemOptions} from '../core/file_system';
import {SyncKeyValueStore, SimpleSyncStore, SimpleSyncRWTransaction, SyncKeyValueRWTransaction, SyncKeyValueFileSystem} from '../generic/key_value_filesystem';

/**
Expand All @@ -7,7 +7,7 @@ import {SyncKeyValueStore, SimpleSyncStore, SimpleSyncRWTransaction, SyncKeyValu
export class InMemoryStore implements SyncKeyValueStore, SimpleSyncStore {
private store: { [key: string]: Buffer } = {};

public name() { return 'In-memory'; }
public name() { return InMemoryFileSystem.Name; }
public clear() { this.store = {}; }

public beginTransaction(type: string): SyncKeyValueRWTransaction {
Expand Down Expand Up @@ -36,14 +36,15 @@ export class InMemoryStore implements SyncKeyValueStore, SimpleSyncStore {
* Files are not persisted across page loads.
*/
export default class InMemoryFileSystem extends SyncKeyValueFileSystem {
public static readonly Name = "InMemory";

public static readonly Options: FileSystemOptions = {};

/**
* Creates an InMemoryFileSystem instance.
*/
public static Create(cb: BFSCallback<InMemoryFileSystem>): void;
public static Create(options: any, cb: BFSCallback<InMemoryFileSystem>): void;
public static Create(options: any, cb?: any): void {
const normalizedCb: BFSCallback<InMemoryFileSystem> = cb ? cb : options;
normalizedCb(null, new InMemoryFileSystem());
public static Create(options: any, cb: BFSCallback<InMemoryFileSystem>): void {
cb(null, new InMemoryFileSystem());
}
constructor() {
super({ store: new InMemoryStore() });
Expand Down
23 changes: 15 additions & 8 deletions src/backend/IndexedDB.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BFSOneArgCallback, BFSCallback} from '../core/file_system';
import {BFSOneArgCallback, BFSCallback, FileSystemOptions} from '../core/file_system';
import {AsyncKeyValueROTransaction, AsyncKeyValueRWTransaction, AsyncKeyValueStore, AsyncKeyValueFileSystem} from '../generic/key_value_filesystem';
import {ApiError, ErrorCode} from '../core/api_error';
import global from '../core/global';
Expand Down Expand Up @@ -155,7 +155,7 @@ export class IndexedDBStore implements AsyncKeyValueStore {
}

public name(): string {
return "IndexedDB - " + this.storeName;
return IndexedDBFileSystem.Name + " - " + this.storeName;
}

public clear(cb: BFSOneArgCallback): void {
Expand Down Expand Up @@ -201,15 +201,22 @@ export interface IndexedDBFileSystemOptions {
* A file system that uses the IndexedDB key value file system.
*/
export default class IndexedDBFileSystem extends AsyncKeyValueFileSystem {
public static readonly Name = "IndexedDB";

public static readonly Options: FileSystemOptions = {
storeName: {
type: "string",
optional: true,
description: "The name of this file system. You can have multiple IndexedDB file systems operating at once, but each must have a different name."
}
};

/**
* Constructs an IndexedDB file system with the given options.
*/
public static Create(cb: BFSCallback<IndexedDBFileSystem>): void;
public static Create(opts: IndexedDBFileSystemOptions, cb: BFSCallback<IndexedDBFileSystem>): void;
public static Create(opts: any, cb?: any): void {
const normalizedCb = cb ? cb : opts;
public static Create(opts: IndexedDBFileSystemOptions, cb: BFSCallback<IndexedDBFileSystem>): void {
// tslint:disable-next-line:no-unused-new
new IndexedDBFileSystem(normalizedCb, cb && opts ? opts['storeName'] : undefined, false);
new IndexedDBFileSystem(cb, opts.storeName, false);
// tslint:enable-next-line:no-unused-new
}
public static isAvailable(): boolean {
Expand Down Expand Up @@ -244,6 +251,6 @@ export default class IndexedDBFileSystem extends AsyncKeyValueFileSystem {
});
}
}, storeName);
deprecationMessage(deprecateMsg, "IndexedDB", {storeName: storeName});
deprecationMessage(deprecateMsg, IndexedDBFileSystem.Name, {storeName: storeName});
}
}
32 changes: 19 additions & 13 deletions src/backend/IsoFS.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {ApiError, ErrorCode} from '../core/api_error';
import {default as Stats, FileType} from '../core/node_fs_stats';
import {SynchronousFileSystem, FileSystem, BFSCallback} from '../core/file_system';
import {SynchronousFileSystem, FileSystem, BFSCallback, FileSystemOptions} from '../core/file_system';
import {File} from '../core/file';
import {FileFlag, ActionType} from '../core/file_flag';
import {NoSyncFile} from '../generic/preload_file';
import {copyingSlice, deprecationMessage} from '../core/util';
import {copyingSlice, deprecationMessage, bufferValidator} from '../core/util';
import * as path from 'path';

/**
Expand Down Expand Up @@ -447,12 +447,10 @@ abstract class DirectoryRecord {
let p = "";
const entries = this.getSUEntries(isoData);
const getStr = this._getGetString();
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
for (const entry of entries) {
if (entry instanceof SLEntry) {
const components = entry.componentRecords();
for (let j = 0; j < components.length; j++) {
const component = components[j];
for (const component of components) {
const flags = component.flags();
if (flags & SLComponentFlags.CURRENT) {
p += "./";
Expand Down Expand Up @@ -514,8 +512,7 @@ abstract class DirectoryRecord {
}
let str = '';
const getString = this._getGetString();
for (let i = 0; i < nmEntries.length; i++) {
const e = nmEntries[i];
for (const e of nmEntries) {
str += e.name(getString);
if (!(e.flags() & NMFlags.CONTINUE)) {
break;
Expand Down Expand Up @@ -1157,6 +1154,16 @@ export interface IsoFSOptions {
* * Microsoft Joliet and Rock Ridge extensions to the ISO9660 standard
*/
export default class IsoFS extends SynchronousFileSystem implements FileSystem {
public static readonly Name = "IsoFS";

public static readonly Options: FileSystemOptions = {
data: {
type: "object",
description: "The ISO file in a buffer",
validator: bufferValidator
}
};

/**
* Creates an IsoFS instance with the given options.
*/
Expand Down Expand Up @@ -1190,7 +1197,7 @@ export default class IsoFS extends SynchronousFileSystem implements FileSystem {
constructor(data: Buffer, name: string = "", deprecateMsg = true) {
super();
this._data = data;
deprecationMessage(deprecateMsg, "IsoFS", {data: "ISO data as a Buffer", name: name});
deprecationMessage(deprecateMsg, IsoFS.Name, {data: "ISO data as a Buffer", name: name});
// Skip first 16 sectors.
let vdTerminatorFound = false;
let i = 16 * 2048;
Expand Down Expand Up @@ -1326,9 +1333,9 @@ export default class IsoFS extends SynchronousFileSystem implements FileSystem {
}
const components = path.split('/').slice(1);
let dir = this._root;
for (let i = 0; i < components.length; i++) {
for (const component of components) {
if (dir.isDirectory(this._data)) {
dir = dir.getDirectory(this._data).getRecord(components[i]);
dir = dir.getDirectory(this._data).getRecord(component);
if (!dir) {
return null;
}
Expand Down Expand Up @@ -1356,8 +1363,7 @@ export default class IsoFS extends SynchronousFileSystem implements FileSystem {
let ctime = date;
if (record.hasRockRidge()) {
const entries = record.getSUEntries(this._data);
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
for (const entry of entries) {
if (entry instanceof PXEntry) {
mode = entry.mode();
} else if (entry instanceof TFEntry) {
Expand Down
Loading

0 comments on commit 297844f

Please sign in to comment.