diff --git a/src/crud/types.ts b/src/crud/types.ts index e3b90583d..aa1eb61dd 100644 --- a/src/crud/types.ts +++ b/src/crud/types.ts @@ -73,7 +73,7 @@ export interface CrudPutOptions { export interface CrudTypeEntry { /** Kind of the resource, type or item. */ - kind: 'type' | 'item'; + type: 'resource' | 'collection'; /** Name of the resource. */ id: string; } @@ -94,5 +94,5 @@ export interface CrudScanResult { export interface CrudScanEntry extends CrudTypeEntry { /** Collection, which contains this entry. */ - type: CrudCollection; + collection: CrudCollection; } diff --git a/src/fsa-crud/FsaCrud.ts b/src/fsa-crud/FsaCrud.ts index 985979b10..d218af450 100644 --- a/src/fsa-crud/FsaCrud.ts +++ b/src/fsa-crud/FsaCrud.ts @@ -75,8 +75,8 @@ export class FsaCrud implements crud.CrudApi { }; public readonly del = async (collection: crud.CrudCollection, id: string, silent?: boolean): Promise => { - assertType(collection, 'get', 'crudfs'); - assertName(id, 'get', 'crudfs'); + assertType(collection, 'del', 'crudfs'); + assertName(id, 'del', 'crudfs'); try { const [dir] = await this.getFile(collection, id); await dir.removeEntry(id, {recursive: false}); @@ -86,7 +86,24 @@ export class FsaCrud implements crud.CrudApi { }; public readonly info = async (collection: crud.CrudCollection, id?: string): Promise => { - throw new Error('Not implemented'); + assertType(collection, 'info', 'crudfs'); + if (id) { + assertName(id, 'info', 'crudfs'); + const [, file] = await this.getFile(collection, id); + const blob = await file.getFile(); + return { + type: 'resource', + id, + size: blob.size, + modified: blob.lastModified, + }; + } else { + await this.getDir(collection, false); + return { + type: 'collection', + id: '', + }; + } }; public readonly drop = async (collection: crud.CrudCollection): Promise => { diff --git a/src/fsa-crud/__tests__/FsaCrud.test.ts b/src/fsa-crud/__tests__/FsaCrud.test.ts index 2003d878f..25ae12fc9 100644 --- a/src/fsa-crud/__tests__/FsaCrud.test.ts +++ b/src/fsa-crud/__tests__/FsaCrud.test.ts @@ -20,14 +20,16 @@ onlyOnNode20('FsaCrud', () => { describe('.put()', () => { test('throws if the type is not valid', async () => { const {crud} = setup(); - const [, err] = await of(crud.put(['', 'foo'], 'bar', new Uint8Array())); + const [, err] = await of(crud.put(['.', 'foo'], 'bar', new Uint8Array())); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'put' on 'crudfs': Name is not allowed."); }); test('throws if id is not valid', async () => { const {crud} = setup(); - const [, err] = await of(crud.put(['foo'], '', new Uint8Array())); + const [, err] = await of(crud.put(['foo'], '..', new Uint8Array())); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'put' on 'crudfs': Name is not allowed."); }); test('can store a resource at root', async () => { @@ -81,12 +83,14 @@ onlyOnNode20('FsaCrud', () => { const {crud} = setup(); const [, err] = await of(crud.get(['', 'foo'], 'bar')); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'get' on 'crudfs': Name is not allowed."); }); test('throws if id is not valid', async () => { const {crud} = setup(); const [, err] = await of(crud.get(['foo'], '')); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'get' on 'crudfs': Name is not allowed."); }); test('throws if collection does not exist', async () => { @@ -115,17 +119,18 @@ onlyOnNode20('FsaCrud', () => { describe('.del()', () => { test('throws if the type is not valid', async () => { const {crud} = setup(); - const [, err] = await of(crud.del(['', 'foo'], 'bar')); + const [, err] = await of(crud.del(['sdf\\dd', 'foo'], 'bar')); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'del' on 'crudfs': Name is not allowed."); }); test('throws if id is not valid', async () => { const {crud} = setup(); - const [, err] = await of(crud.del(['foo'], '')); + const [, err] = await of(crud.del(['foo'], 'asdf/asdf')); expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'del' on 'crudfs': Name is not allowed."); }); - describe('when collection does not exist', () => { test('throws by default', async () => { const {crud} = setup(); @@ -166,4 +171,57 @@ onlyOnNode20('FsaCrud', () => { expect((err).name).toBe('ResourceNotFound'); }); }); + + describe('.info()', () => { + test('throws if the type is not valid', async () => { + const {crud} = setup(); + const [, err] = await of(crud.info(['', 'foo'], 'bar')); + expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'info' on 'crudfs': Name is not allowed."); + }); + + test('throws if id is not valid', async () => { + const {crud} = setup(); + const [, err] = await of(crud.info(['foo'], '/')); + expect(err).toBeInstanceOf(TypeError); + expect((err).message).toBe("Failed to execute 'info' on 'crudfs': Name is not allowed."); + }); + + test('can retrieve information about a resource', async () => { + const {crud} = setup(); + await crud.put(['foo'], 'bar', b('abc')); + const info = await crud.info(['foo'], 'bar'); + expect(info).toMatchObject({ + type: 'resource', + id: 'bar', + size: 3, + modified: expect.any(Number), + }); + }); + + test('can retrieve information about a collection', async () => { + const {crud} = setup(); + await crud.put(['foo'], 'bar', b('abc')); + const info = await crud.info(['foo']); + expect(info).toMatchObject({ + type: 'collection', + }); + }); + + test('throws when resource not found', async () => { + const {crud} = setup(); + await crud.put(['foo'], 'bar', b('abc')); + const [, err] = await of(crud.info(['foo'], 'baz')); + expect(err).toBeInstanceOf(DOMException); + expect((err).name).toBe('ResourceNotFound'); + }); + + test('throws when collection not found', async () => { + const {crud} = setup(); + await crud.put(['foo', 'a'], 'bar', b('abc')); + const [, err] = await of(crud.info(['foo', 'b'], 'baz')); + expect(err).toBeInstanceOf(DOMException); + expect((err).name).toBe('CollectionNotFound'); + }); + }); });