Skip to content

Commit

Permalink
Merge bcc31b3 into 4ac9e92
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimvh committed Jul 23, 2020
2 parents 4ac9e92 + bcc31b3 commit efe488e
Show file tree
Hide file tree
Showing 17 changed files with 758 additions and 105 deletions.
25 changes: 21 additions & 4 deletions bin/server.ts
Expand Up @@ -2,21 +2,29 @@ import yargs from 'yargs';
import {
AcceptPreferenceParser,
AuthenticatedLdpHandler,
BodyParser,
CompositeAsyncHandler,
ExpressHttpServer,
HttpRequest,
Operation,
PatchingStore,
Representation,
ResponseDescription,
SimpleAuthorizer,
SimpleBodyParser,
SimpleCredentialsExtractor,
SimpleDeleteOperationHandler,
SimpleGetOperationHandler,
SimplePatchOperationHandler,
SimplePermissionsExtractor,
SimplePostOperationHandler,
SimpleRequestParser,
SimpleResourceStore,
SimpleResponseWriter,
SimpleSparqlUpdateBodyParser,
SimpleSparqlUpdatePatchHandler,
SimpleTargetExtractor,
SingleThreadedResourceLocker,
} from '..';

const { argv } = yargs
Expand All @@ -29,10 +37,14 @@ const { argv } = yargs
const { port } = argv;

// This is instead of the dependency injection that still needs to be added
const bodyParser: BodyParser = new CompositeAsyncHandler<HttpRequest, Representation>([
new SimpleBodyParser(),
new SimpleSparqlUpdateBodyParser(),
]);
const requestParser = new SimpleRequestParser({
targetExtractor: new SimpleTargetExtractor(),
preferenceParser: new AcceptPreferenceParser(),
bodyParser: new SimpleBodyParser(),
bodyParser,
});

const credentialsExtractor = new SimpleCredentialsExtractor();
Expand All @@ -41,10 +53,15 @@ const authorizer = new SimpleAuthorizer();

// Will have to see how to best handle this
const store = new SimpleResourceStore(`http://localhost:${port}/`);
const locker = new SingleThreadedResourceLocker();
const patcher = new SimpleSparqlUpdatePatchHandler(store, locker);
const patchingStore = new PatchingStore(store, patcher);

const operationHandler = new CompositeAsyncHandler<Operation, ResponseDescription>([
new SimpleGetOperationHandler(store),
new SimplePostOperationHandler(store),
new SimpleDeleteOperationHandler(store),
new SimpleDeleteOperationHandler(patchingStore),
new SimpleGetOperationHandler(patchingStore),
new SimplePatchOperationHandler(patchingStore),
new SimplePostOperationHandler(patchingStore),
]);

const responseWriter = new SimpleResponseWriter();
Expand Down
8 changes: 8 additions & 0 deletions index.ts
Expand Up @@ -17,7 +17,9 @@ export * from './src/ldp/http/ResponseWriter';
export * from './src/ldp/http/SimpleBodyParser';
export * from './src/ldp/http/SimpleRequestParser';
export * from './src/ldp/http/SimpleResponseWriter';
export * from './src/ldp/http/SimpleSparqlUpdateBodyParser';
export * from './src/ldp/http/SimpleTargetExtractor';
export * from './src/ldp/http/SparqlUpdatePatch';
export * from './src/ldp/http/TargetExtractor';

// LDP/Operations
Expand All @@ -26,6 +28,7 @@ export * from './src/ldp/operations/OperationHandler';
export * from './src/ldp/operations/ResponseDescription';
export * from './src/ldp/operations/SimpleDeleteOperationHandler';
export * from './src/ldp/operations/SimpleGetOperationHandler';
export * from './src/ldp/operations/SimplePatchOperationHandler';
export * from './src/ldp/operations/SimplePostOperationHandler';

// LDP/Permissions
Expand All @@ -52,11 +55,16 @@ export * from './src/server/HttpHandler';
export * from './src/server/HttpRequest';
export * from './src/server/HttpResponse';

// Storage/Patch
export * from './src/storage/patch/PatchHandler';
export * from './src/storage/patch/SimpleSparqlUpdatePatchHandler';

// Storage
export * from './src/storage/AtomicResourceStore';
export * from './src/storage/Conditions';
export * from './src/storage/Lock';
export * from './src/storage/LockingResourceStore';
export * from './src/storage/PatchingStore';
export * from './src/storage/RepresentationConverter';
export * from './src/storage/ResourceLocker';
export * from './src/storage/ResourceMapper';
Expand Down
41 changes: 33 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -40,6 +40,8 @@
"cors": "^2.8.5",
"express": "^4.17.1",
"n3": "^1.4.0",
"rdf-terms": "^1.5.1",
"sparqlalgebrajs": "^2.3.1",
"yargs": "^15.4.1"
},
"devDependencies": {
Expand Down
7 changes: 6 additions & 1 deletion src/ldp/http/Patch.ts
Expand Up @@ -3,4 +3,9 @@ import { Representation } from '../representation/Representation';
/**
* Represents the changes needed for a PATCH request.
*/
export interface Patch extends Representation {}
export interface Patch extends Representation {
/**
* The raw body of the PATCH request.
*/
raw: string;
}
47 changes: 47 additions & 0 deletions src/ldp/http/SimpleSparqlUpdateBodyParser.ts
@@ -0,0 +1,47 @@
import { BodyParser } from './BodyParser';
import { HttpRequest } from '../../server/HttpRequest';
import { Readable } from 'stream';
import { readableToString } from '../../util/Util';
import { SparqlUpdatePatch } from './SparqlUpdatePatch';
import { translate } from 'sparqlalgebrajs';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';

/**
* {@link BodyParser} that supports `application/sparql-update` content.
* Will convert the incoming update string to algebra in a {@link SparqlUpdatePatch}.
* Simple since metadata parsing is not yet implemented.
*/
export class SimpleSparqlUpdateBodyParser extends BodyParser {
public async canHandle(input: HttpRequest): Promise<void> {
const contentType = input.headers['content-type'];

if (!contentType || contentType !== 'application/sparql-update') {
throw new UnsupportedMediaTypeHttpError('This parser only supports SPARQL UPDATE data.');
}
}

public async handle(input: HttpRequest): Promise<SparqlUpdatePatch> {
try {
const sparql = await readableToString(input);
const algebra = translate(sparql, { quads: true });

// Prevent body from being requested again
return {
algebra,
dataType: 'sparql-algebra',
raw: sparql,
get data(): Readable {
throw new Error('Body already parsed');
},
metadata: {
raw: [],
profiles: [],
contentType: 'application/sparql-update',
},
};
} catch (error) {
throw new UnsupportedHttpError(error);
}
}
}
12 changes: 12 additions & 0 deletions src/ldp/http/SparqlUpdatePatch.ts
@@ -0,0 +1,12 @@
import { Patch } from './Patch';
import { Update } from 'sparqlalgebrajs/lib/algebra';

/**
* A specific type of {@link Patch} corresponding to a SPARQL update.
*/
export interface SparqlUpdatePatch extends Patch {
/**
* Algebra corresponding to the SPARQL update.
*/
algebra: Update;
}
26 changes: 26 additions & 0 deletions src/ldp/operations/SimplePatchOperationHandler.ts
@@ -0,0 +1,26 @@
import { Operation } from './Operation';
import { OperationHandler } from './OperationHandler';
import { Patch } from '../http/Patch';
import { ResourceStore } from '../../storage/ResourceStore';
import { ResponseDescription } from './ResponseDescription';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';

export class SimplePatchOperationHandler extends OperationHandler {
private readonly store: ResourceStore;

public constructor(store: ResourceStore) {
super();
this.store = store;
}

public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'PATCH') {
throw new UnsupportedHttpError('This handler only supports PATCH operations.');
}
}

public async handle(input: Operation): Promise<ResponseDescription> {
await this.store.modifyResource(input.target, input.body as Patch);
return { identifier: input.target };
}
}
46 changes: 46 additions & 0 deletions src/storage/PatchingStore.ts
@@ -0,0 +1,46 @@
import { Conditions } from './Conditions';
import { Patch } from '../ldp/http/Patch';
import { PatchHandler } from './patch/PatchHandler';
import { Representation } from '../ldp/representation/Representation';
import { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences';
import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { ResourceStore } from './ResourceStore';

/**
* {@link ResourceStore} using decorator pattern for the `modifyResource` function.
* If the original store supports the {@link Patch}, behaviour will be identical,
* otherwise one of the {@link PatchHandler}s supporting the given Patch will be called instead.
*/
export class PatchingStore implements ResourceStore {
private readonly source: ResourceStore;
private readonly patcher: PatchHandler;

public constructor(source: ResourceStore, patcher: PatchHandler) {
this.source = source;
this.patcher = patcher;
}

public async addResource(container: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<ResourceIdentifier> {
return this.source.addResource(container, representation, conditions);
}

public async deleteResource(identifier: ResourceIdentifier, conditions?: Conditions): Promise<void> {
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 setRepresentation(identifier: ResourceIdentifier, representation: Representation, conditions?: Conditions): Promise<void> {
return this.source.setRepresentation(identifier, representation, conditions);
}

public async modifyResource(identifier: ResourceIdentifier, patch: Patch, conditions?: Conditions): Promise<void> {
try {
return await this.source.modifyResource(identifier, patch, conditions);
} catch (error) {
return this.patcher.handleSafe({ identifier, patch });
}
}
}
5 changes: 5 additions & 0 deletions src/storage/patch/PatchHandler.ts
@@ -0,0 +1,5 @@
import { AsyncHandler } from '../../util/AsyncHandler';
import { Patch } from '../../ldp/http/Patch';
import { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';

export abstract class PatchHandler extends AsyncHandler<{identifier: ResourceIdentifier; patch: Patch}> {}

0 comments on commit efe488e

Please sign in to comment.