feat: Added resourceExists method to ResourceStore

* feat: added resourceExists method to ResourceStore

* Merge remote-tracking branch 'origin/main' into feat/add-resourceExists-method-to-ResourceStore

* fix: adapted to review

* fix: adapted to review
This commit is contained in:
Arne Vandoorslaer
2021-02-25 13:43:58 +01:00
committed by GitHub
parent e5b7d99da4
commit b3f292d718
24 changed files with 136 additions and 67 deletions

View File

@@ -4,7 +4,6 @@ import { BasicRepresentation } from '../ldp/representation/BasicRepresentation';
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { getLoggerFor } from '../logging/LogUtil';
import type { ResourceStore } from '../storage/ResourceStore';
import { containsResource } from '../storage/StoreUtil';
import { TEXT_TURTLE } from '../util/ContentTypes';
import { ensureTrailingSlash, joinFilePath } from '../util/PathUtil';
import { Initializer } from './Initializer';
@@ -39,7 +38,7 @@ export class AclInitializer extends Initializer {
// https://solid.github.io/specification/protocol#storage
public async handle(): Promise<void> {
const rootAcl = this.aclStrategy.getAuxiliaryIdentifier(this.root);
if (await containsResource(this.store, rootAcl)) {
if (await this.store.resourceExists(rootAcl)) {
this.logger.debug(`Existing root ACL document found at ${rootAcl.path}`);
} else {
this.logger.debug(`Installing root ACL document at ${rootAcl.path}`);

View File

@@ -4,7 +4,6 @@ import { RepresentationMetadata } from '../ldp/representation/RepresentationMeta
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { getLoggerFor } from '../logging/LogUtil';
import type { ResourceStore } from '../storage/ResourceStore';
import { containsResource } from '../storage/StoreUtil';
import { TEXT_TURTLE } from '../util/ContentTypes';
import { ensureTrailingSlash } from '../util/PathUtil';
import { generateResourceQuads } from '../util/ResourceUtil';
@@ -32,7 +31,7 @@ export class RootContainerInitializer extends Initializer {
public async handle(): Promise<void> {
this.logger.debug(`Checking for root container at ${this.baseId.path}`);
if (!await containsResource(this.store, this.baseId)) {
if (!await this.store.resourceExists(this.baseId)) {
await this.createRootContainer();
} else {
this.logger.debug(`Existing root container found at ${this.baseId.path}`);

View File

@@ -1,7 +1,6 @@
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { getLoggerFor } from '../logging/LogUtil';
import type { ResourceStore } from '../storage/ResourceStore';
import { containsResource } from '../storage/StoreUtil';
import { ConflictHttpError } from '../util/errors/ConflictHttpError';
import type { Agent } from './agent/Agent';
import type { IdentifierGenerator } from './generate/IdentifierGenerator';
@@ -33,7 +32,7 @@ export class GeneratedPodManager implements PodManager {
public async createPod(agent: Agent): Promise<ResourceIdentifier> {
const podIdentifier = this.idGenerator.generate(agent.login);
this.logger.info(`Creating pod ${podIdentifier.path}`);
if (await containsResource(this.store, podIdentifier)) {
if (await this.store.resourceExists(podIdentifier)) {
throw new ConflictHttpError(`There already is a resource at ${podIdentifier.path}`);
}

View File

@@ -11,6 +11,10 @@ import type { ResourceStore } from './ResourceStore';
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
export class BaseResourceStore implements ResourceStore {
public async resourceExists(identifier: ResourceIdentifier, conditions?: Conditions): Promise<boolean> {
throw new NotImplementedHttpError();
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
throw new NotImplementedHttpError();

View File

@@ -67,6 +67,19 @@ export class DataAccessorBasedStore implements ResourceStore {
this.auxiliaryStrategy = auxiliaryStrategy;
}
public async resourceExists(identifier: ResourceIdentifier): Promise<boolean> {
try {
this.validateIdentifier(identifier);
await this.accessor.getMetadata(identifier);
return true;
} catch (error: unknown) {
if (NotFoundHttpError.isInstance(error)) {
return false;
}
throw error;
}
}
public async getRepresentation(identifier: ResourceIdentifier): Promise<Representation> {
this.validateIdentifier(identifier);

View File

@@ -35,6 +35,11 @@ export class LockingResourceStore implements AtomicResourceStore {
this.strategy = strategy;
}
public async resourceExists(identifier: ResourceIdentifier, conditions?: Conditions): Promise<boolean> {
return this.locks.withReadLock(this.getLockIdentifier(identifier),
async(): Promise<boolean> => this.source.resourceExists(identifier, conditions));
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.lockedRepresentationRun(this.getLockIdentifier(identifier),

View File

@@ -19,6 +19,10 @@ export class MonitoringStore<T extends ResourceStore = ResourceStore>
this.source = source;
}
public async resourceExists(identifier: ResourceIdentifier, conditions?: Conditions): Promise<boolean> {
return this.source.resourceExists(identifier, conditions);
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.source.getRepresentation(identifier, preferences, conditions);

View File

@@ -17,6 +17,15 @@ export class PassthroughStore<T extends ResourceStore = ResourceStore> implement
this.source = source;
}
public async resourceExists(identifier: ResourceIdentifier, conditions?: Conditions): Promise<boolean> {
return this.source.resourceExists(identifier, conditions);
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.source.getRepresentation(identifier, preferences, conditions);
}
public async addResource(container: ResourceIdentifier, representation: Representation,
conditions?: Conditions): Promise<ResourceIdentifier> {
return this.source.addResource(container, representation, conditions);
@@ -27,11 +36,6 @@ export class PassthroughStore<T extends ResourceStore = ResourceStore> implement
return this.source.deleteResource(identifier, conditions);
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return this.source.getRepresentation(identifier, preferences, conditions);
}
public async modifyResource(identifier: ResourceIdentifier, patch: Patch,
conditions?: Conditions): Promise<ResourceIdentifier[]> {
return this.source.modifyResource(identifier, patch, conditions);

View File

@@ -16,6 +16,15 @@ import type { Conditions } from './Conditions';
* should those be relevant to the store.
*/
export interface ResourceStore {
/**
* Check if a resource exists.
* @param identifier - Identifier of resource to check.
*
* @returns A promise resolving if the resource already exists
*/
resourceExists: (identifier: ResourceIdentifier, conditions?: Conditions) => Promise<boolean>;
/**
* Retrieves a representation of a resource.
* @param identifier - Identifier of the resource to read.

View File

@@ -20,6 +20,11 @@ export class RoutingResourceStore implements ResourceStore {
this.rule = rule;
}
public async resourceExists(identifier: ResourceIdentifier, conditions?: Conditions):
Promise<boolean> {
return (await this.getStore(identifier)).resourceExists(identifier, conditions);
}
public async getRepresentation(identifier: ResourceIdentifier, preferences: RepresentationPreferences,
conditions?: Conditions): Promise<Representation> {
return (await this.getStore(identifier)).getRepresentation(identifier, preferences, conditions);

View File

@@ -1,16 +0,0 @@
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
import type { ResourceStore } from './ResourceStore';
export async function containsResource(store: ResourceStore, identifier: ResourceIdentifier): Promise<boolean> {
try {
const result = await store.getRepresentation(identifier, {});
result.data.destroy();
return true;
} catch (error: unknown) {
if (NotFoundHttpError.isInstance(error)) {
return false;
}
throw error;
}
}

View File

@@ -1,6 +1,5 @@
import type { Representation } from '../../ldp/representation/Representation';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import { containsResource } from '../../storage/StoreUtil';
import type { ResourceStore } from '../ResourceStore';
import type { PreferenceSupport } from './PreferenceSupport';
import { RouterRule } from './RouterRule';
@@ -40,8 +39,7 @@ export class ConvertingRouterRule extends RouterRule {
entry.supportChecker.supports({ identifier, representation }));
} else {
// No content-type given so we can only check if one of the stores has data for the identifier
store = await this.findStore(async(entry): Promise<boolean> =>
containsResource(entry.store, input.identifier));
store = await this.findStore(async(entry): Promise<boolean> => entry.store.resourceExists(input.identifier));
}
return store;
}