mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Implement a first draft of the RoutingResourceStore
This commit is contained in:
parent
dee4eef131
commit
86de805daa
45
src/storage/RoutingResourceStore.ts
Normal file
45
src/storage/RoutingResourceStore.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { Patch } from '../ldp/http/Patch';
|
||||||
|
import type { Representation } from '../ldp/representation/Representation';
|
||||||
|
import type { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences';
|
||||||
|
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
|
||||||
|
import type { Conditions } from './Conditions';
|
||||||
|
import type { ResourceStore } from './ResourceStore';
|
||||||
|
import type { RouterRule } from './routing/RouterRule';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store that calls a specific store based on certain routing defined by the ResourceRouter.
|
||||||
|
*/
|
||||||
|
export class RoutingResourceStore implements ResourceStore {
|
||||||
|
private readonly rule: RouterRule;
|
||||||
|
|
||||||
|
public constructor(rule: RouterRule) {
|
||||||
|
this.rule = rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
|
||||||
|
conditions?: Conditions): Promise<Representation> {
|
||||||
|
return (await this.rule.getMatchingResourceStore(identifier))
|
||||||
|
.getRepresentation(identifier, preferences, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addResource(container: ResourceIdentifier, representation: Representation,
|
||||||
|
conditions?: Conditions): Promise<ResourceIdentifier> {
|
||||||
|
return (await this.rule.getMatchingResourceStore(container, representation))
|
||||||
|
.addResource(container, representation, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setRepresentation(identifier: ResourceIdentifier, representation: Representation,
|
||||||
|
conditions?: Conditions): Promise<void> {
|
||||||
|
return (await this.rule.getMatchingResourceStore(identifier, representation))
|
||||||
|
.setRepresentation(identifier, representation, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise<void> {
|
||||||
|
return (await this.rule.getMatchingResourceStore(identifier)).deleteResource(identifier, conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async modifyResource(identifier: ResourceIdentifier, patch: Patch, conditions?: Conditions):
|
||||||
|
Promise<void> {
|
||||||
|
return (await this.rule.getMatchingResourceStore(identifier)).modifyResource(identifier, patch, conditions);
|
||||||
|
}
|
||||||
|
}
|
35
src/storage/routing/PathRouterRule.ts
Normal file
35
src/storage/routing/PathRouterRule.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import type { Representation } from '../../ldp/representation/Representation';
|
||||||
|
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||||
|
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||||
|
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||||
|
import type { ResourceStore } from '../ResourceStore';
|
||||||
|
import type { RouterRule } from './RouterRule';
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
export class PathRouterRule implements RouterRule {
|
||||||
|
private readonly pathMap: { [path: string]: ResourceStore };
|
||||||
|
|
||||||
|
public constructor(pathMap: { [path: string]: ResourceStore }) {
|
||||||
|
this.pathMap = pathMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMatchingResourceStore(identifier: ResourceIdentifier, representation?: Representation):
|
||||||
|
Promise<ResourceStore> {
|
||||||
|
const paths = Object.keys(this.pathMap);
|
||||||
|
const matches = paths.filter((path): boolean => identifier.path.includes(path));
|
||||||
|
if (matches.length !== 1) {
|
||||||
|
// Incoming data, need to reject
|
||||||
|
if (representation) {
|
||||||
|
throw new UnsupportedHttpError(
|
||||||
|
`Identifiers need to have exactly 1 of the following in them: [${paths.join(', ')}]`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Because of the above requirement, we know this will always be a 404 for requests
|
||||||
|
} else {
|
||||||
|
throw new NotFoundHttpError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.pathMap[matches[0]];
|
||||||
|
}
|
||||||
|
}
|
42
src/storage/routing/RdfConvertingRouterRule.ts
Normal file
42
src/storage/routing/RdfConvertingRouterRule.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import type { Representation } from '../../ldp/representation/Representation';
|
||||||
|
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||||
|
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||||
|
import type { RepresentationConverter } from '../conversion/RepresentationConverter';
|
||||||
|
import type { ResourceStore } from '../ResourceStore';
|
||||||
|
import type { RouterRule } from './RouterRule';
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
export class RdfConvertingRouterRule implements RouterRule {
|
||||||
|
private readonly rdfStore: ResourceStore;
|
||||||
|
private readonly binaryStore: ResourceStore;
|
||||||
|
private readonly converter: RepresentationConverter;
|
||||||
|
|
||||||
|
public constructor(rdfStore: ResourceStore, binaryStore: ResourceStore, converter: RepresentationConverter) {
|
||||||
|
this.rdfStore = rdfStore;
|
||||||
|
this.binaryStore = binaryStore;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMatchingResourceStore(identifier: ResourceIdentifier, representation?: Representation):
|
||||||
|
Promise<ResourceStore> {
|
||||||
|
if (representation) {
|
||||||
|
try {
|
||||||
|
const preferences = { type: [{ value: INTERNAL_QUADS, weight: 1 }]};
|
||||||
|
await this.converter.canHandle({ identifier, representation, preferences });
|
||||||
|
return this.rdfStore;
|
||||||
|
} catch {
|
||||||
|
return this.binaryStore;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No content-type given so we can only check if one of the stores has data for the identifier
|
||||||
|
// Any of the two stores can be used. Using the binary one here since that one would be faster in current cases.
|
||||||
|
try {
|
||||||
|
const response = await this.binaryStore.getRepresentation(identifier, {});
|
||||||
|
response.data.destroy();
|
||||||
|
return this.binaryStore;
|
||||||
|
} catch {
|
||||||
|
return this.rdfStore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/storage/routing/RouterRule.ts
Normal file
20
src/storage/routing/RouterRule.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Representation } from '../../ldp/representation/Representation';
|
||||||
|
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||||
|
import type { ResourceStore } from '../ResourceStore';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A RouterRule represents a rule that decides which instance of a
|
||||||
|
* ResourceStore should be used to handle the incoming request.
|
||||||
|
*/
|
||||||
|
export interface RouterRule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the appropriate ResourceStore to which the request should be routed based on the incoming parameters.
|
||||||
|
* @param identifier - Incoming ResourceIdentifier.
|
||||||
|
* @param representation - Optional incoming Representation.
|
||||||
|
*/
|
||||||
|
getMatchingResourceStore: (
|
||||||
|
identifier: ResourceIdentifier,
|
||||||
|
representation?: Representation,
|
||||||
|
) => Promise<ResourceStore>;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user