fix: Make Patch more consistent with other Representations

This commit is contained in:
Joachim Van Herwegen 2020-08-04 10:05:48 +02:00
parent 14db5fed91
commit d6a35f9954
6 changed files with 24 additions and 18 deletions

View File

@ -4,8 +4,4 @@ import { Representation } from '../representation/Representation';
* Represents the changes needed for a PATCH request. * 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;
} }

View File

@ -1,6 +1,7 @@
import { BodyParser } from './BodyParser'; import { BodyParser } from './BodyParser';
import { DATA_TYPE_BINARY } from '../../util/ContentTypes';
import { HttpRequest } from '../../server/HttpRequest'; import { HttpRequest } from '../../server/HttpRequest';
import { Readable } from 'stream'; import { PassThrough } from 'stream';
import { readableToString } from '../../util/Util'; import { readableToString } from '../../util/Util';
import { SparqlUpdatePatch } from './SparqlUpdatePatch'; import { SparqlUpdatePatch } from './SparqlUpdatePatch';
import { translate } from 'sparqlalgebrajs'; import { translate } from 'sparqlalgebrajs';
@ -23,17 +24,21 @@ export class SimpleSparqlUpdateBodyParser extends BodyParser {
public async handle(input: HttpRequest): Promise<SparqlUpdatePatch> { public async handle(input: HttpRequest): Promise<SparqlUpdatePatch> {
try { try {
const sparql = await readableToString(input); // Note that readableObjectMode is only defined starting from Node 12
// It is impossible to check if object mode is enabled in Node 10 (without accessing private variables)
const options = { objectMode: input.readableObjectMode };
const toAlgebraStream = new PassThrough(options);
const dataCopy = new PassThrough(options);
input.pipe(toAlgebraStream);
input.pipe(dataCopy);
const sparql = await readableToString(toAlgebraStream);
const algebra = translate(sparql, { quads: true }); const algebra = translate(sparql, { quads: true });
// Prevent body from being requested again // Prevent body from being requested again
return { return {
algebra, algebra,
dataType: 'sparql-algebra', dataType: DATA_TYPE_BINARY,
raw: sparql, data: dataCopy,
get data(): Readable {
throw new Error('Body already parsed');
},
metadata: { metadata: {
raw: [], raw: [],
profiles: [], profiles: [],

View File

@ -28,7 +28,7 @@ export class SimpleSparqlUpdatePatchHandler extends PatchHandler {
} }
public async canHandle(input: {identifier: ResourceIdentifier; patch: SparqlUpdatePatch}): Promise<void> { public async canHandle(input: {identifier: ResourceIdentifier; patch: SparqlUpdatePatch}): Promise<void> {
if (input.patch.dataType !== 'sparql-algebra' || !input.patch.algebra) { if (typeof input.patch.algebra !== 'object') {
throw new UnsupportedHttpError('Only SPARQL update patch requests are supported.'); throw new UnsupportedHttpError('Only SPARQL update patch requests are supported.');
} }
} }

View File

@ -120,8 +120,8 @@ describe('An AuthenticatedLdpHandler', (): void => {
describe('with simple PATCH handlers', (): void => { describe('with simple PATCH handlers', (): void => {
const bodyParser: BodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([ const bodyParser: BodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([
new SimpleBodyParser(),
new SimpleSparqlUpdateBodyParser(), new SimpleSparqlUpdateBodyParser(),
new SimpleBodyParser(),
]); ]);
const requestParser = new SimpleRequestParser({ const requestParser = new SimpleRequestParser({
targetExtractor: new SimpleTargetExtractor(), targetExtractor: new SimpleTargetExtractor(),

View File

@ -1,4 +1,6 @@
import { Algebra } from 'sparqlalgebrajs'; import { Algebra } from 'sparqlalgebrajs';
import arrayifyStream from 'arrayify-stream';
import { DATA_TYPE_BINARY } from '../../../../src/util/ContentTypes';
import { HttpRequest } from '../../../../src/server/HttpRequest'; import { HttpRequest } from '../../../../src/server/HttpRequest';
import { SimpleSparqlUpdateBodyParser } from '../../../../src/ldp/http/SimpleSparqlUpdateBodyParser'; import { SimpleSparqlUpdateBodyParser } from '../../../../src/ldp/http/SimpleSparqlUpdateBodyParser';
import streamifyArray from 'streamify-array'; import streamifyArray from 'streamify-array';
@ -32,13 +34,16 @@ describe('A SimpleSparqlUpdateBodyParser', (): void => {
namedNode('http://test.com/p'), namedNode('http://test.com/p'),
namedNode('http://test.com/o'), namedNode('http://test.com/o'),
) ]); ) ]);
expect(result.dataType).toBe('sparql-algebra'); expect(result.dataType).toBe(DATA_TYPE_BINARY);
expect(result.raw).toBe('DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o>}');
expect(result.metadata).toEqual({ expect(result.metadata).toEqual({
raw: [], raw: [],
profiles: [], profiles: [],
contentType: 'application/sparql-update', contentType: 'application/sparql-update',
}); });
expect((): any => result.data).toThrow('Body already parsed');
// Workaround for Node 10 not exposing objectMode
expect((await arrayifyStream(result.data)).join('')).toEqual(
'DELETE DATA { <http://test.com/s> <http://test.com/p> <http://test.com/o>}',
);
}); });
}); });

View File

@ -80,9 +80,9 @@ describe('A SimpleSparqlUpdatePatchHandler', (): void => {
it('only accepts SPARQL updates.', async(): Promise<void> => { it('only accepts SPARQL updates.', async(): Promise<void> => {
const input = { identifier: { path: 'path' }, const input = { identifier: { path: 'path' },
patch: { dataType: 'sparql-algebra', algebra: {}} as SparqlUpdatePatch }; patch: { algebra: {}} as SparqlUpdatePatch };
await expect(handler.canHandle(input)).resolves.toBeUndefined(); await expect(handler.canHandle(input)).resolves.toBeUndefined();
input.patch.dataType = 'notAlgebra'; delete input.patch.algebra;
await expect(handler.canHandle(input)).rejects.toThrow(UnsupportedHttpError); await expect(handler.canHandle(input)).rejects.toThrow(UnsupportedHttpError);
}); });