Skip to content

Commit

Permalink
feat: 🎸 implement .drop() method
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 20, 2023
1 parent eea9215 commit 1b893a2
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 9 deletions.
4 changes: 3 additions & 1 deletion src/crud/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ export interface CrudApi {
* Deletes all resources of a collection, and deletes recursively all sub-collections.
*
* @param collection Type of the resource, collection name.
* @param silent When true, does not throw an error if the collection or
* resource does not exist. Default is false.
*/
drop: (collection: CrudCollection) => Promise<void>;
drop: (collection: CrudCollection, silent?: boolean) => Promise<void>;

/**
* Fetches a list of resources of a collection, and sub-collections.
Expand Down
32 changes: 24 additions & 8 deletions src/fsa-crud/FsaCrud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import {assertType} from './util';
export class FsaCrud implements crud.CrudApi {
public constructor (protected readonly root: fsa.IFileSystemDirectoryHandle | Promise<fsa.IFileSystemDirectoryHandle>) {}

protected async getDir(collection: crud.CrudCollection, create: boolean): Promise<fsa.IFileSystemDirectoryHandle> {
protected async getDir(collection: crud.CrudCollection, create: boolean): Promise<[dir: fsa.IFileSystemDirectoryHandle, parent: fsa.IFileSystemDirectoryHandle | undefined]> {
let parent: undefined | fsa.IFileSystemDirectoryHandle = undefined;
let dir = await this.root;
try {
for (const name of collection)
dir = await dir.getDirectoryHandle(name, {create});
return dir;
for (const name of collection) {
const child = await dir.getDirectoryHandle(name, {create});
parent = dir;
dir = child;
}
return [dir, parent];
} catch (error) {
if (error.name === 'NotFoundError')
throw new DOMException(`Collection /${collection.join('/')} does not exist`, 'CollectionNotFound');
Expand All @@ -20,7 +24,7 @@ export class FsaCrud implements crud.CrudApi {
}

protected async getFile(collection: crud.CrudCollection, id: string): Promise<[dir: fsa.IFileSystemDirectoryHandle, file: fsa.IFileSystemFileHandle]> {
const dir = await this.getDir(collection, false);
const [dir] = await this.getDir(collection, false);
try {
const file = await dir.getFileHandle(id, {create: false});
return [dir, file];
Expand All @@ -34,7 +38,7 @@ export class FsaCrud implements crud.CrudApi {
public readonly put = async (collection: crud.CrudCollection, id: string, data: Uint8Array, options?: crud.CrudPutOptions): Promise<void> => {
assertType(collection, 'put', 'crudfs');
assertName(id, 'put', 'crudfs');
const dir = await this.getDir(collection, true);
const [dir] = await this.getDir(collection, true);
let file: fsa.IFileSystemFileHandle | undefined;
switch (options?.throwIf) {
case 'exists': {
Expand Down Expand Up @@ -106,8 +110,20 @@ export class FsaCrud implements crud.CrudApi {
}
};

public readonly drop = async (collection: crud.CrudCollection): Promise<void> => {
throw new Error('Not implemented');
public readonly drop = async (collection: crud.CrudCollection, silent?: boolean): Promise<void> => {
assertType(collection, 'drop', 'crudfs');
try {
const [dir, parent] = await this.getDir(collection, false);
if (parent) {
await parent.removeEntry(dir.name, {recursive: true});
} else {
const root = await this.root;
for await (const name of root.keys())
await root.removeEntry(name, {recursive: true});
}
} catch (error) {
if (!silent) throw error;
}
};

public readonly list = async (collection: crud.CrudCollection): Promise<crud.CrudTypeEntry[]> => {
Expand Down
51 changes: 51 additions & 0 deletions src/fsa-crud/__tests__/FsaCrud.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,55 @@ onlyOnNode20('FsaCrud', () => {
expect((<any>err).name).toBe('CollectionNotFound');
});
});

describe('.drop()', () => {
test('throws if the collection is not valid', async () => {
const {crud} = setup();
const [, err] = await of(crud.drop(['', 'foo']));
expect(err).toBeInstanceOf(TypeError);
expect((<any>err).message).toBe("Failed to execute 'drop' on 'crudfs': Name is not allowed.");
});

test('can recursively delete a collection', async () => {
const {crud} = setup();
await crud.put(['foo', 'a'], 'bar', b('1'));
await crud.put(['foo', 'a'], 'baz', b('2'));
await crud.put(['foo', 'b'], 'xyz', b('3'));
const info = await crud.info(['foo', 'a']);
expect(info.type).toBe('collection');
await crud.drop(['foo', 'a']);
const [, err] = await of(crud.info(['foo', 'a']));
expect(err).toBeInstanceOf(DOMException);
expect((<any>err).name).toBe('CollectionNotFound');
});

test('throws if collection does not exist', async () => {
const {crud} = setup();
await crud.put(['foo', 'a'], 'bar', b('1'));
await crud.put(['foo', 'a'], 'baz', b('2'));
await crud.put(['foo', 'b'], 'xyz', b('3'));
const [, err] = await of(crud.drop(['gg']));
expect(err).toBeInstanceOf(DOMException);
expect((<any>err).name).toBe('CollectionNotFound');
});

test('when "silent" flag set, does not throw if collection does not exist', async () => {
const {crud} = setup();
await crud.put(['foo', 'a'], 'bar', b('1'));
await crud.put(['foo', 'a'], 'baz', b('2'));
await crud.put(['foo', 'b'], 'xyz', b('3'));
await crud.drop(['gg'], true);
});

test('can recursively delete everything from root', async () => {
const {crud, snapshot} = setup();
await crud.put(['foo', 'a'], 'bar', b('1'));
await crud.put(['baz', 'a'], 'baz', b('2'));
await crud.put(['bar', 'b'], 'xyz', b('3'));
const info = await crud.info(['foo', 'a']);
expect(info.type).toBe('collection');
await crud.drop([]);
expect(snapshot()).toEqual({});
});
});
});

0 comments on commit 1b893a2

Please sign in to comment.