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