feat: Update KeyValueStorage interface with entries function

This commit is contained in:
Joachim Van Herwegen 2021-02-25 16:59:14 +01:00
parent 52551ac773
commit 0f00a8dffd
8 changed files with 33 additions and 1 deletions

View File

@ -1,6 +1,7 @@
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation'; import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier'; import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError'; import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { readableToString } from '../../util/StreamUtil'; import { readableToString } from '../../util/StreamUtil';
import type { ResourceStore } from '../ResourceStore'; import type { ResourceStore } from '../ResourceStore';
import type { KeyValueStorage } from './KeyValueStorage'; import type { KeyValueStorage } from './KeyValueStorage';
@ -61,4 +62,9 @@ export class JsonResourceStorage implements KeyValueStorage<ResourceIdentifier,
return false; return false;
} }
} }
public entries(): never {
// We don't know which resources are in the store
throw new NotImplementedHttpError();
}
} }

View File

@ -33,4 +33,9 @@ export interface KeyValueStorage<TKey, TValue> {
* @returns If there was a value to delete. * @returns If there was a value to delete.
*/ */
delete: (key: TKey) => Promise<boolean>; delete: (key: TKey) => Promise<boolean>;
/**
* An iterable of entries in the storage.
*/
entries: () => AsyncIterableIterator<[TKey, TValue]>;
} }

View File

@ -28,4 +28,10 @@ export class MemoryMapStorage<TKey, TValue> implements KeyValueStorage<TKey, TVa
public async delete(key: TKey): Promise<boolean> { public async delete(key: TKey): Promise<boolean> {
return this.data.delete(key); return this.data.delete(key);
} }
public async* entries(): AsyncIterableIterator<[TKey, TValue]> {
for (const entry of this.data.entries()) {
yield entry;
}
}
} }

View File

@ -30,4 +30,10 @@ export class ResourceIdentifierStorage<T> implements KeyValueStorage<ResourceIde
public async delete(key: ResourceIdentifier): Promise<boolean> { public async delete(key: ResourceIdentifier): Promise<boolean> {
return this.source.delete(key.path); return this.source.delete(key.path);
} }
public async* entries(): AsyncIterableIterator<[ResourceIdentifier, T]> {
for await (const [ path, value ] of this.source.entries()) {
yield [{ path }, value ];
}
}
} }

View File

@ -4,6 +4,7 @@ import type { ResourceIdentifier } from '../../../../src/ldp/representation/Reso
import { JsonResourceStorage } from '../../../../src/storage/keyvalue/JsonResourceStorage'; import { JsonResourceStorage } from '../../../../src/storage/keyvalue/JsonResourceStorage';
import type { ResourceStore } from '../../../../src/storage/ResourceStore'; import type { ResourceStore } from '../../../../src/storage/ResourceStore';
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError'; import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
import { readableToString } from '../../../../src/util/StreamUtil'; import { readableToString } from '../../../../src/util/StreamUtil';
describe('A JsonResourceStorage', (): void => { describe('A JsonResourceStorage', (): void => {
@ -44,6 +45,7 @@ describe('A JsonResourceStorage', (): void => {
it('returns data if it was set beforehand.', async(): Promise<void> => { it('returns data if it was set beforehand.', async(): Promise<void> => {
await expect(storage.set(identifier1, 'apple')).resolves.toBe(storage); await expect(storage.set(identifier1, 'apple')).resolves.toBe(storage);
await expect(storage.get(identifier1)).resolves.toBe('apple'); await expect(storage.get(identifier1)).resolves.toBe('apple');
expect(storage.entries).toThrow(NotImplementedHttpError);
}); });
it('can check if data is present.', async(): Promise<void> => { it('can check if data is present.', async(): Promise<void> => {

View File

@ -17,6 +17,7 @@ describe('A MemoryMapStorage', (): void => {
it('returns data if it was set beforehand.', async(): Promise<void> => { it('returns data if it was set beforehand.', async(): Promise<void> => {
await expect(storage.set(identifier1, 'apple')).resolves.toBe(storage); await expect(storage.set(identifier1, 'apple')).resolves.toBe(storage);
await expect(storage.get(identifier1)).resolves.toBe('apple'); await expect(storage.get(identifier1)).resolves.toBe('apple');
await expect(storage.entries().next()).resolves.toEqual({ done: false, value: [ identifier1, 'apple' ]});
}); });
it('can check if data is present.', async(): Promise<void> => { it('can check if data is present.', async(): Promise<void> => {

View File

@ -13,6 +13,9 @@ describe('A ResourceIdentifierStorage', (): void => {
has: jest.fn(), has: jest.fn(),
set: jest.fn(), set: jest.fn(),
delete: jest.fn(), delete: jest.fn(),
entries: jest.fn(async function* (): any {
yield [ 'a', 1 ];
}),
}; };
storage = new ResourceIdentifierStorage(source); storage = new ResourceIdentifierStorage(source);
}); });
@ -33,5 +36,8 @@ describe('A ResourceIdentifierStorage', (): void => {
await storage.delete(identifier); await storage.delete(identifier);
expect(source.delete).toHaveBeenCalledTimes(1); expect(source.delete).toHaveBeenCalledTimes(1);
expect(source.delete).toHaveBeenLastCalledWith(path); expect(source.delete).toHaveBeenLastCalledWith(path);
await storage.entries().next();
expect(source.entries).toHaveBeenCalledTimes(1);
}); });
}); });

View File

@ -52,7 +52,7 @@ describe('A GreedyReadWriteLocker', (): void => {
has: async(identifier: ResourceIdentifier): Promise<boolean> => map.has(identifier.path), has: async(identifier: ResourceIdentifier): Promise<boolean> => map.has(identifier.path),
set: async(identifier: ResourceIdentifier, value: number): Promise<any> => map.set(identifier.path, value), set: async(identifier: ResourceIdentifier, value: number): Promise<any> => map.set(identifier.path, value),
delete: async(identifier: ResourceIdentifier): Promise<boolean> => map.delete(identifier.path), delete: async(identifier: ResourceIdentifier): Promise<boolean> => map.delete(identifier.path),
}; } as any;
locker = new GreedyReadWriteLocker(sourceLocker, storage); locker = new GreedyReadWriteLocker(sourceLocker, storage);
}); });