From 4403421c49e02b851c29e3cb29f248f00f03f639 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Tue, 4 Aug 2020 10:24:59 +0200 Subject: [PATCH] feat: Integrate data conversion with rest of server --- bin/server.ts | 21 ++-- src/ldp/http/SimpleBodyParser.ts | 42 ++----- src/storage/SimpleResourceStore.ts | 104 +++++++----------- src/util/CompositeAsyncHandler.ts | 2 + .../AuthenticatedLdpHandler.test.ts | 12 +- test/integration/RequestParser.test.ts | 13 +-- test/unit/ldp/http/SimpleBodyParser.test.ts | 47 ++------ test/unit/storage/SimpleResourceStore.test.ts | 87 +++------------ 8 files changed, 102 insertions(+), 226 deletions(-) diff --git a/bin/server.ts b/bin/server.ts index 152943d45..ae4bdc2fb 100644 --- a/bin/server.ts +++ b/bin/server.ts @@ -2,14 +2,13 @@ import yargs from 'yargs'; import { AcceptPreferenceParser, AuthenticatedLdpHandler, - BodyParser, CompositeAsyncHandler, ExpressHttpServer, HttpRequest, - Operation, PatchingStore, + QuadToTurtleConverter, Representation, - ResponseDescription, + RepresentationConvertingStore, SimpleAuthorizer, SimpleBodyParser, SimpleCredentialsExtractor, @@ -25,6 +24,7 @@ import { SimpleSparqlUpdatePatchHandler, SimpleTargetExtractor, SingleThreadedResourceLocker, + TurtleToQuadConverter, } from '..'; const { argv } = yargs @@ -37,9 +37,9 @@ const { argv } = yargs const { port } = argv; // This is instead of the dependency injection that still needs to be added -const bodyParser: BodyParser = new CompositeAsyncHandler([ - new SimpleBodyParser(), +const bodyParser = new CompositeAsyncHandler([ new SimpleSparqlUpdateBodyParser(), + new SimpleBodyParser(), ]); const requestParser = new SimpleRequestParser({ targetExtractor: new SimpleTargetExtractor(), @@ -53,11 +53,16 @@ const authorizer = new SimpleAuthorizer(); // Will have to see how to best handle this const store = new SimpleResourceStore(`http://localhost:${port}/`); +const converter = new CompositeAsyncHandler([ + new TurtleToQuadConverter(), + new QuadToTurtleConverter(), +]); +const convertingStore = new RepresentationConvertingStore(store, converter); const locker = new SingleThreadedResourceLocker(); -const patcher = new SimpleSparqlUpdatePatchHandler(store, locker); -const patchingStore = new PatchingStore(store, patcher); +const patcher = new SimpleSparqlUpdatePatchHandler(convertingStore, locker); +const patchingStore = new PatchingStore(convertingStore, patcher); -const operationHandler = new CompositeAsyncHandler([ +const operationHandler = new CompositeAsyncHandler([ new SimpleDeleteOperationHandler(patchingStore), new SimpleGetOperationHandler(patchingStore), new SimplePatchOperationHandler(patchingStore), diff --git a/src/ldp/http/SimpleBodyParser.ts b/src/ldp/http/SimpleBodyParser.ts index 2736667bb..cd794dc7a 100644 --- a/src/ldp/http/SimpleBodyParser.ts +++ b/src/ldp/http/SimpleBodyParser.ts @@ -1,37 +1,22 @@ +import { BinaryRepresentation } from '../representation/BinaryRepresentation'; import { BodyParser } from './BodyParser'; -import { DATA_TYPE_QUAD } from '../../util/ContentTypes'; +import { DATA_TYPE_BINARY } from '../../util/ContentTypes'; import { HttpRequest } from '../../server/HttpRequest'; -import { PassThrough } from 'stream'; -import { QuadRepresentation } from '../representation/QuadRepresentation'; import { RepresentationMetadata } from '../representation/RepresentationMetadata'; -import { StreamParser } from 'n3'; -import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; -import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError'; /** - * Parses the incoming {@link HttpRequest} if there is no body or if it contains turtle (or similar) RDF data. - * Naively parses the content-type header to determine the body type. + * Converts incoming {@link HttpRequest} to a Representation without any further parsing. + * Naively parses the mediatype from the content-type header. + * Metadata is not generated (yet). */ export class SimpleBodyParser extends BodyParser { - private static readonly contentTypes = [ - 'application/n-quads', - 'application/trig', - 'application/n-triples', - 'text/turtle', - 'text/n3', - ]; - - public async canHandle(input: HttpRequest): Promise { - const contentType = input.headers['content-type']; - - if (contentType && !SimpleBodyParser.contentTypes.some((type): boolean => contentType.includes(type))) { - throw new UnsupportedMediaTypeHttpError('This parser only supports RDF data.'); - } + public async canHandle(): Promise { + // Default BodyParser supports all content-types } // Note that the only reason this is a union is in case the body is empty. // If this check gets moved away from the BodyParsers this union could be removed - public async handle(input: HttpRequest): Promise { + public async handle(input: HttpRequest): Promise { const contentType = input.headers['content-type']; if (!contentType) { @@ -46,16 +31,9 @@ export class SimpleBodyParser extends BodyParser { contentType: mediaType, }; - // Catch parsing errors and emit correct error - // Node 10 requires both writableObjectMode and readableObjectMode - const errorStream = new PassThrough({ writableObjectMode: true, readableObjectMode: true }); - const data = input.pipe(new StreamParser()); - data.pipe(errorStream); - data.on('error', (error): boolean => errorStream.emit('error', new UnsupportedHttpError(error.message))); - return { - dataType: DATA_TYPE_QUAD, - data: errorStream, + dataType: DATA_TYPE_BINARY, + data: input, metadata, }; } diff --git a/src/storage/SimpleResourceStore.ts b/src/storage/SimpleResourceStore.ts index e530ff3b4..57f547dd4 100644 --- a/src/storage/SimpleResourceStore.ts +++ b/src/storage/SimpleResourceStore.ts @@ -1,24 +1,18 @@ import arrayifyStream from 'arrayify-stream'; -import { BinaryRepresentation } from '../ldp/representation/BinaryRepresentation'; +import { DATA_TYPE_BINARY } from '../util/ContentTypes'; import { ensureTrailingSlash } from '../util/Util'; import { NotFoundHttpError } from '../util/errors/NotFoundHttpError'; -import { Quad } from 'rdf-js'; -import { QuadRepresentation } from '../ldp/representation/QuadRepresentation'; import { Representation } from '../ldp/representation/Representation'; -import { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences'; import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; import { ResourceStore } from './ResourceStore'; import streamifyArray from 'streamify-array'; -import { StreamWriter } from 'n3'; -import { UnsupportedMediaTypeHttpError } from '../util/errors/UnsupportedMediaTypeHttpError'; -import { CONTENT_TYPE_QUADS, DATA_TYPE_BINARY, DATA_TYPE_QUAD } from '../util/ContentTypes'; /** - * Resource store storing its data as Quads in an in-memory map. + * Resource store storing its data in an in-memory map. * All requests will throw an {@link NotFoundHttpError} if unknown identifiers get passed. */ export class SimpleResourceStore implements ResourceStore { - private readonly store: { [id: string]: Quad[] } = { '': []}; + private readonly store: { [id: string]: Representation }; private readonly base: string; private index = 0; @@ -27,13 +21,22 @@ export class SimpleResourceStore implements ResourceStore { */ public constructor(base: string) { this.base = base; + + this.store = { + // Default root entry (what you get when the identifier is equal to the base) + '': { + dataType: DATA_TYPE_BINARY, + data: streamifyArray([]), + metadata: { raw: [], profiles: []}, + }, + }; } /** * Stores the incoming data under a new URL corresponding to `container.path + number`. * Slash added when needed. * @param container - The identifier to store the new data under. - * @param representation - Data to store. Only Quad streams are supported. + * @param representation - Data to store. * * @returns The newly generated identifier. */ @@ -41,7 +44,7 @@ export class SimpleResourceStore implements ResourceStore { const containerPath = this.parseIdentifier(container); const newPath = `${ensureTrailingSlash(containerPath)}${this.index}`; this.index += 1; - this.store[newPath] = await this.parseRepresentation(representation); + this.store[newPath] = await this.copyRepresentation(representation); return { path: `${this.base}${newPath}` }; } @@ -57,18 +60,15 @@ export class SimpleResourceStore implements ResourceStore { /** * Returns the stored representation for the given identifier. - * The only preference that is supported is `type === 'text/turtle'`. - * In all other cases a stream of Quads will be returned. + * Preferences will be ignored, data will be returned as it was received. * * @param identifier - Identifier to retrieve. - * @param preferences - Preferences for resulting Representation. * * @returns The corresponding Representation. */ - public async getRepresentation(identifier: ResourceIdentifier, - preferences: RepresentationPreferences): Promise { + public async getRepresentation(identifier: ResourceIdentifier): Promise { const path = this.parseIdentifier(identifier); - return this.generateRepresentation(this.store[path], preferences); + return this.generateRepresentation(path); } /** @@ -85,7 +85,7 @@ export class SimpleResourceStore implements ResourceStore { */ public async setRepresentation(identifier: ResourceIdentifier, representation: Representation): Promise { const path = this.parseIdentifier(identifier); - this.store[path] = await this.parseRepresentation(representation); + this.store[path] = await this.copyRepresentation(representation); } /** @@ -106,66 +106,36 @@ export class SimpleResourceStore implements ResourceStore { } /** - * Converts the Representation to an array of Quads. - * @param representation - Incoming Representation. - * @throws {@link UnsupportedMediaTypeHttpError} - * If the representation is not a Quad stream. + * Copies the Representation by draining the original data stream and creating a new one. * - * @returns Promise of array of Quads pulled from the stream. + * @param data - Incoming Representation. */ - private async parseRepresentation(representation: Representation): Promise { - if (representation.dataType !== DATA_TYPE_QUAD) { - throw new UnsupportedMediaTypeHttpError('SimpleResourceStore only supports quad representations.'); - } - return arrayifyStream(representation.data); + private async copyRepresentation(source: Representation): Promise { + const arr = await arrayifyStream(source.data); + return { + dataType: source.dataType, + data: streamifyArray([ ...arr ]), + metadata: source.metadata, + }; } /** - * Converts an array of Quads to a Representation. - * If preferences.type contains 'text/turtle' the result will be a stream of turtle strings, - * otherwise a stream of Quads. + * Generates a Representation that is identical to the one stored, + * but makes sure to duplicate the data stream so it stays readable for later calls. * - * Note that in general this should be done by resource store specifically made for converting to turtle, - * this is just here to make this simple resource store work. - * - * @param data - Quads to transform. - * @param preferences - Requested preferences. + * @param path - Path in store of Representation. * * @returns The resulting Representation. */ - private generateRepresentation(data: Quad[], preferences: RepresentationPreferences): Representation { - // Always return turtle unless explicitly asked for quads - if (preferences.type?.some((preference): boolean => preference.value.includes(CONTENT_TYPE_QUADS))) { - return this.generateQuadRepresentation(data); - } - return this.generateBinaryRepresentation(data); - } + private async generateRepresentation(path: string): Promise { + const source = this.store[path]; + const arr = await arrayifyStream(source.data); + source.data = streamifyArray([ ...arr ]); - /** - * Creates a {@link BinaryRepresentation} of the incoming Quads. - * @param data - Quads to transform to text/turtle. - * - * @returns The resulting binary Representation. - */ - private generateBinaryRepresentation(data: Quad[]): BinaryRepresentation { return { - dataType: DATA_TYPE_BINARY, - data: streamifyArray([ ...data ]).pipe(new StreamWriter({ format: 'text/turtle' })), - metadata: { raw: [], profiles: [], contentType: 'text/turtle' }, - }; - } - - /** - * Creates a {@link QuadRepresentation} of the incoming Quads. - * @param data - Quads to transform to a stream of Quads. - * - * @returns The resulting quad Representation. - */ - private generateQuadRepresentation(data: Quad[]): QuadRepresentation { - return { - dataType: DATA_TYPE_QUAD, - data: streamifyArray([ ...data ]), - metadata: { raw: [], profiles: []}, + dataType: source.dataType, + data: streamifyArray([ ...arr ]), + metadata: source.metadata, }; } } diff --git a/src/util/CompositeAsyncHandler.ts b/src/util/CompositeAsyncHandler.ts index f2221b0bd..d024c3a85 100644 --- a/src/util/CompositeAsyncHandler.ts +++ b/src/util/CompositeAsyncHandler.ts @@ -4,6 +4,8 @@ import { UnsupportedHttpError } from './errors/UnsupportedHttpError'; /** * Handler that combines several other handlers, * thereby allowing other classes that depend on a single handler to still use multiple. + * The handlers will be checked in the order they appear in the input array, + * allowing for more fine-grained handlers to check before catch-all handlers. */ export class CompositeAsyncHandler implements AsyncHandler { private readonly handlers: AsyncHandler[]; diff --git a/test/integration/AuthenticatedLdpHandler.test.ts b/test/integration/AuthenticatedLdpHandler.test.ts index 13eaa54d3..b5018b78d 100644 --- a/test/integration/AuthenticatedLdpHandler.test.ts +++ b/test/integration/AuthenticatedLdpHandler.test.ts @@ -9,7 +9,9 @@ import { IncomingHttpHeaders } from 'http'; import { Operation } from '../../src/ldp/operations/Operation'; import { Parser } from 'n3'; import { PatchingStore } from '../../src/storage/PatchingStore'; +import { QuadToTurtleConverter } from '../../src/storage/conversion/QuadToTurtleConverter'; import { Representation } from '../../src/ldp/representation/Representation'; +import { RepresentationConvertingStore } from '../../src/storage/RepresentationConvertingStore'; import { ResponseDescription } from '../../src/ldp/operations/ResponseDescription'; import { SimpleAuthorizer } from '../../src/authorization/SimpleAuthorizer'; import { SimpleBodyParser } from '../../src/ldp/http/SimpleBodyParser'; @@ -27,6 +29,7 @@ import { SimpleSparqlUpdatePatchHandler } from '../../src/storage/patch/SimpleSp import { SimpleTargetExtractor } from '../../src/ldp/http/SimpleTargetExtractor'; import { SingleThreadedResourceLocker } from '../../src/storage/SingleThreadedResourceLocker'; import streamifyArray from 'streamify-array'; +import { TurtleToQuadConverter } from '../../src/storage/conversion/TurtleToQuadConverter'; import { createResponse, MockResponse } from 'node-mocks-http'; import { namedNode, quad } from '@rdfjs/data-model'; import * as url from 'url'; @@ -134,9 +137,14 @@ describe('An AuthenticatedLdpHandler', (): void => { const authorizer = new SimpleAuthorizer(); const store = new SimpleResourceStore('http://test.com/'); + const converter = new CompositeAsyncHandler([ + new QuadToTurtleConverter(), + new TurtleToQuadConverter(), + ]); + const convertingStore = new RepresentationConvertingStore(store, converter); const locker = new SingleThreadedResourceLocker(); - const patcher = new SimpleSparqlUpdatePatchHandler(store, locker); - const patchingStore = new PatchingStore(store, patcher); + const patcher = new SimpleSparqlUpdatePatchHandler(convertingStore, locker); + const patchingStore = new PatchingStore(convertingStore, patcher); const operationHandler = new CompositeAsyncHandler([ new SimpleGetOperationHandler(patchingStore), diff --git a/test/integration/RequestParser.test.ts b/test/integration/RequestParser.test.ts index 2fd796d98..5a006218a 100644 --- a/test/integration/RequestParser.test.ts +++ b/test/integration/RequestParser.test.ts @@ -1,13 +1,12 @@ import { AcceptPreferenceParser } from '../../src/ldp/http/AcceptPreferenceParser'; import arrayifyStream from 'arrayify-stream'; -import { DATA_TYPE_QUAD } from '../../src/util/ContentTypes'; +import { DATA_TYPE_BINARY } from '../../src/util/ContentTypes'; import { HttpRequest } from '../../src/server/HttpRequest'; import { Readable } from 'stream'; import { SimpleBodyParser } from '../../src/ldp/http/SimpleBodyParser'; import { SimpleRequestParser } from '../../src/ldp/http/SimpleRequestParser'; import { SimpleTargetExtractor } from '../../src/ldp/http/SimpleTargetExtractor'; import streamifyArray from 'streamify-array'; -import { namedNode, triple } from '@rdfjs/data-model'; describe('A SimpleRequestParser with simple input parsers', (): void => { const targetExtractor = new SimpleTargetExtractor(); @@ -36,7 +35,7 @@ describe('A SimpleRequestParser with simple input parsers', (): void => { }, body: { data: expect.any(Readable), - dataType: DATA_TYPE_QUAD, + dataType: DATA_TYPE_BINARY, metadata: { contentType: 'text/turtle', profiles: [], @@ -45,10 +44,8 @@ describe('A SimpleRequestParser with simple input parsers', (): void => { }, }); - await expect(arrayifyStream(result.body!.data)).resolves.toEqualRdfQuadArray([ triple( - namedNode('http://test.com/s'), - namedNode('http://test.com/p'), - namedNode('http://test.com/o'), - ) ]); + await expect(arrayifyStream(result.body!.data)).resolves.toEqual( + [ ' .' ], + ); }); }); diff --git a/test/unit/ldp/http/SimpleBodyParser.test.ts b/test/unit/ldp/http/SimpleBodyParser.test.ts index 47984cadc..86eec302a 100644 --- a/test/unit/ldp/http/SimpleBodyParser.test.ts +++ b/test/unit/ldp/http/SimpleBodyParser.test.ts @@ -1,68 +1,37 @@ import arrayifyStream from 'arrayify-stream'; -import { DATA_TYPE_QUAD } from '../../../../src/util/ContentTypes'; +import { DATA_TYPE_BINARY } from '../../../../src/util/ContentTypes'; import { HttpRequest } from '../../../../src/server/HttpRequest'; import { Readable } from 'stream'; import { SimpleBodyParser } from '../../../../src/ldp/http/SimpleBodyParser'; import streamifyArray from 'streamify-array'; -import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; -import { UnsupportedMediaTypeHttpError } from '../../../../src/util/errors/UnsupportedMediaTypeHttpError'; -import { namedNode, triple } from '@rdfjs/data-model'; import 'jest-rdf'; -const contentTypes = [ - 'application/n-quads', - 'application/trig', - 'application/n-triples', - 'text/turtle', - 'text/n3', -]; - describe('A SimpleBodyparser', (): void => { const bodyParser = new SimpleBodyParser(); - it('rejects input with unsupported content type.', async(): Promise => { - await expect(bodyParser.canHandle({ headers: { 'content-type': 'application/rdf+xml' }} as HttpRequest)) - .rejects.toThrow(new UnsupportedMediaTypeHttpError('This parser only supports RDF data.')); - }); - - it('accepts input with no content type.', async(): Promise => { - await expect(bodyParser.canHandle({ headers: { }} as HttpRequest)).resolves.toBeUndefined(); - }); - - it('accepts turtle and similar content types.', async(): Promise => { - for (const type of contentTypes) { - await expect(bodyParser.canHandle({ headers: { 'content-type': type }} as HttpRequest)).resolves.toBeUndefined(); - } + it('accepts all input.', async(): Promise => { + await expect(bodyParser.canHandle()).resolves.toBeUndefined(); }); it('returns empty output if there was no content-type.', async(): Promise => { await expect(bodyParser.handle({ headers: { }} as HttpRequest)).resolves.toBeUndefined(); }); - it('returns a stream of quads if there was data.', async(): Promise => { + it('returns a Representation if there was data.', async(): Promise => { const input = streamifyArray([ ' .' ]) as HttpRequest; input.headers = { 'content-type': 'text/turtle' }; const result = (await bodyParser.handle(input))!; expect(result).toEqual({ data: expect.any(Readable), - dataType: DATA_TYPE_QUAD, + dataType: DATA_TYPE_BINARY, metadata: { contentType: 'text/turtle', profiles: [], raw: [], }, }); - await expect(arrayifyStream(result.data)).resolves.toEqualRdfQuadArray([ triple( - namedNode('http://test.com/s'), - namedNode('http://test.com/p'), - namedNode('http://test.com/o'), - ) ]); - }); - - it('throws an UnsupportedHttpError on invalid triple data when reading the stream.', async(): Promise => { - const input = streamifyArray([ ' ' ]) as HttpRequest; - input.headers = { 'content-type': 'text/turtle' }; - const result = (await bodyParser.handle(input))!; - await expect(arrayifyStream(result.data)).rejects.toThrow(UnsupportedHttpError); + await expect(arrayifyStream(result.data)).resolves.toEqual( + [ ' .' ], + ); }); }); diff --git a/test/unit/storage/SimpleResourceStore.test.ts b/test/unit/storage/SimpleResourceStore.test.ts index eb16080e7..c63040c42 100644 --- a/test/unit/storage/SimpleResourceStore.test.ts +++ b/test/unit/storage/SimpleResourceStore.test.ts @@ -1,37 +1,31 @@ import arrayifyStream from 'arrayify-stream'; +import { BinaryRepresentation } from '../../../src/ldp/representation/BinaryRepresentation'; +import { DATA_TYPE_BINARY } from '../../../src/util/ContentTypes'; import { NotFoundHttpError } from '../../../src/util/errors/NotFoundHttpError'; -import { QuadRepresentation } from '../../../src/ldp/representation/QuadRepresentation'; import { Readable } from 'stream'; import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata'; import { SimpleResourceStore } from '../../../src/storage/SimpleResourceStore'; import streamifyArray from 'streamify-array'; -import { UnsupportedMediaTypeHttpError } from '../../../src/util/errors/UnsupportedMediaTypeHttpError'; -import { CONTENT_TYPE_QUADS, DATA_TYPE_BINARY, DATA_TYPE_QUAD } from '../../../src/util/ContentTypes'; -import { namedNode, triple } from '@rdfjs/data-model'; const base = 'http://test.com/'; describe('A SimpleResourceStore', (): void => { let store: SimpleResourceStore; - let representation: QuadRepresentation; - const quad = triple( - namedNode('http://test.com/s'), - namedNode('http://test.com/p'), - namedNode('http://test.com/o'), - ); + let representation: BinaryRepresentation; + const dataString = ' .'; beforeEach(async(): Promise => { store = new SimpleResourceStore(base); representation = { - data: streamifyArray([ quad ]), - dataType: DATA_TYPE_QUAD, + data: streamifyArray([ dataString ]), + dataType: DATA_TYPE_BINARY, metadata: {} as RepresentationMetadata, }; }); it('errors if a resource was not found.', async(): Promise => { - await expect(store.getRepresentation({ path: `${base}wrong` }, {})).rejects.toThrow(NotFoundHttpError); + await expect(store.getRepresentation({ path: `${base}wrong` })).rejects.toThrow(NotFoundHttpError); await expect(store.addResource({ path: 'http://wrong.com/wrong' }, representation)) .rejects.toThrow(NotFoundHttpError); await expect(store.deleteResource({ path: 'wrong' })).rejects.toThrow(NotFoundHttpError); @@ -43,85 +37,38 @@ describe('A SimpleResourceStore', (): void => { await expect(store.modifyResource()).rejects.toThrow(Error); }); - it('errors for wrong input data types.', async(): Promise => { - (representation as any).dataType = DATA_TYPE_BINARY; - await expect(store.addResource({ path: base }, representation)).rejects.toThrow(UnsupportedMediaTypeHttpError); - }); - it('can write and read data.', async(): Promise => { const identifier = await store.addResource({ path: base }, representation); expect(identifier.path.startsWith(base)).toBeTruthy(); - const result = await store.getRepresentation(identifier, { type: [{ value: CONTENT_TYPE_QUADS, weight: 1 }]}); + const result = await store.getRepresentation(identifier); expect(result).toEqual({ - dataType: DATA_TYPE_QUAD, + dataType: representation.dataType, data: expect.any(Readable), - metadata: { - profiles: [], - raw: [], - }, + metadata: representation.metadata, }); - await expect(arrayifyStream(result.data)).resolves.toEqualRdfQuadArray([ quad ]); + await expect(arrayifyStream(result.data)).resolves.toEqual([ dataString ]); }); it('can add resources to previously added resources.', async(): Promise => { const identifier = await store.addResource({ path: base }, representation); - representation.data = streamifyArray([ quad ]); + representation.data = streamifyArray([ ]); const childIdentifier = await store.addResource(identifier, representation); expect(childIdentifier.path).toContain(identifier.path); }); - it('can read binary data.', async(): Promise => { - const identifier = await store.addResource({ path: base }, representation); - expect(identifier.path.startsWith(base)).toBeTruthy(); - const result = await store.getRepresentation(identifier, { type: [{ value: 'text/turtle', weight: 1 }]}); - expect(result).toEqual({ - dataType: DATA_TYPE_BINARY, - data: expect.any(Readable), - metadata: { - profiles: [], - raw: [], - contentType: 'text/turtle', - }, - }); - await expect(arrayifyStream(result.data)).resolves.toContain( - `<${quad.subject.value}> <${quad.predicate.value}> <${quad.object.value}>`, - ); - }); - - it('returns turtle data if no preference was set.', async(): Promise => { - const identifier = await store.addResource({ path: base }, representation); - expect(identifier.path.startsWith(base)).toBeTruthy(); - const result = await store.getRepresentation(identifier, { }); - expect(result).toEqual({ - dataType: DATA_TYPE_BINARY, - data: expect.any(Readable), - metadata: { - profiles: [], - raw: [], - contentType: 'text/turtle', - }, - }); - await expect(arrayifyStream(result.data)).resolves.toContain( - `<${quad.subject.value}> <${quad.predicate.value}> <${quad.object.value}>`, - ); - }); - it('can set data.', async(): Promise => { await store.setRepresentation({ path: base }, representation); - const result = await store.getRepresentation({ path: base }, { type: [{ value: CONTENT_TYPE_QUADS, weight: 1 }]}); + const result = await store.getRepresentation({ path: base }); expect(result).toEqual({ - dataType: DATA_TYPE_QUAD, + dataType: representation.dataType, data: expect.any(Readable), - metadata: { - profiles: [], - raw: [], - }, + metadata: representation.metadata, }); - await expect(arrayifyStream(result.data)).resolves.toEqualRdfQuadArray([ quad ]); + await expect(arrayifyStream(result.data)).resolves.toEqual([ dataString ]); }); it('can delete data.', async(): Promise => { await store.deleteResource({ path: base }); - await expect(store.getRepresentation({ path: base }, {})).rejects.toThrow(NotFoundHttpError); + await expect(store.getRepresentation({ path: base })).rejects.toThrow(NotFoundHttpError); }); });