mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Create a RoutingResourceStore that takes routing rules
This commit is contained in:
61
src/storage/routing/RegexRouterRule.ts
Normal file
61
src/storage/routing/RegexRouterRule.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { Representation } from '../../ldp/representation/Representation';
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||
import { trimTrailingSlashes } from '../../util/Util';
|
||||
import type { ResourceStore } from '../ResourceStore';
|
||||
import { RouterRule } from './RouterRule';
|
||||
|
||||
/**
|
||||
* Routes requests to a store based on the path of the identifier.
|
||||
* The identifier will be stripped of the base URI after which regexes will be used to find the correct store.
|
||||
* The trailing slash of the base URI will still be present so the first character a regex can match would be that `/`.
|
||||
* This way regexes such as `/container/` can match containers in any position.
|
||||
*
|
||||
* In case none of the regexes match an error will be thrown.
|
||||
*/
|
||||
export class RegexRouterRule extends RouterRule {
|
||||
private readonly base: string;
|
||||
private readonly regexes: Map<RegExp, ResourceStore>;
|
||||
|
||||
/**
|
||||
* The keys of the `storeMap` will be converted into actual RegExp objects that will be used for testing.
|
||||
*/
|
||||
public constructor(base: string, storeMap: Record<string, ResourceStore>) {
|
||||
super();
|
||||
this.base = trimTrailingSlashes(base);
|
||||
this.regexes = new Map(Object.keys(storeMap).map((regex): [ RegExp, ResourceStore ] =>
|
||||
[ new RegExp(regex, 'u'), storeMap[regex] ]));
|
||||
}
|
||||
|
||||
public async canHandle(input: { identifier: ResourceIdentifier; representation?: Representation }): Promise<void> {
|
||||
this.matchStore(input.identifier);
|
||||
}
|
||||
|
||||
public async handle(input: { identifier: ResourceIdentifier }): Promise<ResourceStore> {
|
||||
return this.matchStore(input.identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the store corresponding to the regex that matches the given identifier.
|
||||
* Throws an error if none is found.
|
||||
*/
|
||||
private matchStore(identifier: ResourceIdentifier): ResourceStore {
|
||||
const path = this.toRelative(identifier);
|
||||
for (const regex of this.regexes.keys()) {
|
||||
if (regex.test(path)) {
|
||||
return this.regexes.get(regex)!;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedHttpError(`No stored regexes match ${identifier.path}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the base of the identifier and throws an error if there is no overlap.
|
||||
*/
|
||||
private toRelative(identifier: ResourceIdentifier): string {
|
||||
if (!identifier.path.startsWith(this.base)) {
|
||||
throw new UnsupportedHttpError(`Identifiers need to start with ${this.base}`);
|
||||
}
|
||||
return identifier.path.slice(this.base.length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user