mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Introduce internal storing mechanism
This commit is contained in:
@@ -144,6 +144,12 @@ export * from './storage/conversion/RdfToQuadConverter';
|
||||
export * from './storage/conversion/RepresentationConverter';
|
||||
export * from './storage/conversion/TypedRepresentationConverter';
|
||||
|
||||
// Storage/KeyValueStorage
|
||||
export * from './storage/keyvalue/JsonResourceStorage';
|
||||
export * from './storage/keyvalue/KeyValueStorage';
|
||||
export * from './storage/keyvalue/MemoryMapStorage';
|
||||
export * from './storage/keyvalue/ResourceIdentifierStorage';
|
||||
|
||||
// Storage/Mapping
|
||||
export * from './storage/mapping/BaseFileIdentifierMapper';
|
||||
export * from './storage/mapping/ExtensionBasedMapper';
|
||||
|
||||
64
src/storage/keyvalue/JsonResourceStorage.ts
Normal file
64
src/storage/keyvalue/JsonResourceStorage.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||
import { readableToString } from '../../util/StreamUtil';
|
||||
import type { ResourceStore } from '../ResourceStore';
|
||||
import type { KeyValueStorage } from './KeyValueStorage';
|
||||
|
||||
/**
|
||||
* A {@link KeyValueStorage} for strings using a {@link ResourceStore} as backend.
|
||||
*
|
||||
* Values will be sent as data streams to the given identifiers,
|
||||
* so how these are stored depend on the underlying store.
|
||||
*
|
||||
* All non-404 errors will be re-thrown.
|
||||
*/
|
||||
export class JsonResourceStorage implements KeyValueStorage<ResourceIdentifier, unknown> {
|
||||
private readonly source: ResourceStore;
|
||||
|
||||
public constructor(source: ResourceStore) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public async get(identifier: ResourceIdentifier): Promise<unknown | undefined> {
|
||||
try {
|
||||
const representation = await this.source.getRepresentation(identifier, { type: { 'application/json': 1 }});
|
||||
return JSON.parse(await readableToString(representation.data));
|
||||
} catch (error: unknown) {
|
||||
if (!NotFoundHttpError.isInstance(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async has(identifier: ResourceIdentifier): Promise<boolean> {
|
||||
try {
|
||||
const representation = await this.source.getRepresentation(identifier, { type: { 'application/json': 1 }});
|
||||
representation.data.destroy();
|
||||
return true;
|
||||
} catch (error: unknown) {
|
||||
if (!NotFoundHttpError.isInstance(error)) {
|
||||
throw error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async set(identifier: ResourceIdentifier, value: unknown): Promise<this> {
|
||||
const representation = new BasicRepresentation(JSON.stringify(value), identifier, 'application/json');
|
||||
await this.source.setRepresentation(identifier, representation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async delete(identifier: ResourceIdentifier): Promise<boolean> {
|
||||
try {
|
||||
await this.source.deleteResource(identifier);
|
||||
return true;
|
||||
} catch (error: unknown) {
|
||||
if (!NotFoundHttpError.isInstance(error)) {
|
||||
throw error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/storage/keyvalue/KeyValueStorage.ts
Normal file
36
src/storage/keyvalue/KeyValueStorage.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* A simple storage solution that can be used for internal values that need to be stored.
|
||||
* In general storages taking objects as keys are expected to work with different instances
|
||||
* of an object with the same values. Exceptions to this expectation should be clearly documented.
|
||||
*/
|
||||
export interface KeyValueStorage<TKey, TValue> {
|
||||
/**
|
||||
* Returns the value stored for the given identifier.
|
||||
* `undefined` if no value is stored.
|
||||
* @param identifier - Identifier to get the value for.
|
||||
*/
|
||||
get: (key: TKey) => Promise<TValue | undefined>;
|
||||
|
||||
/**
|
||||
* Checks if there is a value stored for the given key.
|
||||
* @param identifier - Identifier to check.
|
||||
*/
|
||||
has: (key: TKey) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Sets the value for the given key.
|
||||
* @param key - Key to set/update.
|
||||
* @param value - Value to store.
|
||||
*
|
||||
* @returns The storage.
|
||||
*/
|
||||
set: (key: TKey, value: TValue) => Promise<this>;
|
||||
|
||||
/**
|
||||
* Deletes the value stored for the given key.
|
||||
* @param key - Key to delete.
|
||||
*
|
||||
* @returns If there was a value to delete.
|
||||
*/
|
||||
delete: (key: TKey) => Promise<boolean>;
|
||||
}
|
||||
31
src/storage/keyvalue/MemoryMapStorage.ts
Normal file
31
src/storage/keyvalue/MemoryMapStorage.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { KeyValueStorage } from './KeyValueStorage';
|
||||
|
||||
/**
|
||||
* A {@link KeyValueStorage} which uses a JavaScript Map for internal storage.
|
||||
* Warning: Uses a Map object, which internally uses `Object.is` for key equality,
|
||||
* so object keys have to be the same objects.
|
||||
*/
|
||||
export class MemoryMapStorage<TKey, TValue> implements KeyValueStorage<TKey, TValue> {
|
||||
private readonly data: Map<TKey, TValue>;
|
||||
|
||||
public constructor() {
|
||||
this.data = new Map<TKey, TValue>();
|
||||
}
|
||||
|
||||
public async get(key: TKey): Promise<TValue | undefined> {
|
||||
return this.data.get(key);
|
||||
}
|
||||
|
||||
public async has(key: TKey): Promise<boolean> {
|
||||
return this.data.has(key);
|
||||
}
|
||||
|
||||
public async set(key: TKey, value: TValue): Promise<this> {
|
||||
this.data.set(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async delete(key: TKey): Promise<boolean> {
|
||||
return this.data.delete(key);
|
||||
}
|
||||
}
|
||||
33
src/storage/keyvalue/ResourceIdentifierStorage.ts
Normal file
33
src/storage/keyvalue/ResourceIdentifierStorage.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import type { KeyValueStorage } from './KeyValueStorage';
|
||||
|
||||
/**
|
||||
* Wrapper class that internally converts ResourceIdentifiers to strings so Storages
|
||||
* that do not check value equivalence can be used with ResourceIdentifiers.
|
||||
*
|
||||
* Specifically: this makes it so a Storage based on a Map object can be used with ResourceIdentifiers.
|
||||
*/
|
||||
export class ResourceIdentifierStorage<T> implements KeyValueStorage<ResourceIdentifier, T> {
|
||||
private readonly source: KeyValueStorage<string, T>;
|
||||
|
||||
public constructor(source: KeyValueStorage<string, T>) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public async get(key: ResourceIdentifier): Promise<T | undefined> {
|
||||
return this.source.get(key.path);
|
||||
}
|
||||
|
||||
public async has(key: ResourceIdentifier): Promise<boolean> {
|
||||
return this.source.has(key.path);
|
||||
}
|
||||
|
||||
public async set(key: ResourceIdentifier, value: T): Promise<this> {
|
||||
await this.source.set(key.path, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public async delete(key: ResourceIdentifier): Promise<boolean> {
|
||||
return this.source.delete(key.path);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user