feat: Implement a first draft of the RoutingResourceStore

This commit is contained in:
iesmessa 2020-08-28 17:42:02 +02:00 committed by Joachim Van Herwegen
parent dee4eef131
commit 86de805daa
4 changed files with 142 additions and 0 deletions

View 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);
}
}

View 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]];
}
}

View 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;
}
}
}
}

View 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>;
}