mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add BasicRepresentation.
This commit is contained in:
parent
be1af89b56
commit
66e636878f
@ -1,4 +1,5 @@
|
||||
[
|
||||
"BasicRepresentation",
|
||||
"Error",
|
||||
"EventEmitter",
|
||||
"ValuePreferencesArg"
|
||||
|
@ -71,6 +71,7 @@ export * from './ldp/permissions/MethodPermissionsExtractor';
|
||||
export * from './ldp/permissions/SparqlPatchPermissionsExtractor';
|
||||
|
||||
// LDP/Representation
|
||||
export * from './ldp/representation/BasicRepresentation';
|
||||
export * from './ldp/representation/Representation';
|
||||
export * from './ldp/representation/RepresentationMetadata';
|
||||
export * from './ldp/representation/RepresentationPreferences';
|
||||
|
@ -1,12 +1,11 @@
|
||||
import type { AclManager } from '../authorization/AclManager';
|
||||
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
|
||||
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 { TEXT_TURTLE } from '../util/ContentTypes';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
import { ensureTrailingSlash } from '../util/PathUtil';
|
||||
import { guardedStreamFrom } from '../util/StreamUtil';
|
||||
import { Initializer } from './Initializer';
|
||||
|
||||
/**
|
||||
@ -66,15 +65,7 @@ export class AclInitializer extends Initializer {
|
||||
acl:mode acl:Control;
|
||||
acl:accessTo <${this.baseUrl}>;
|
||||
acl:default <${this.baseUrl}>.`;
|
||||
const metadata = new RepresentationMetadata(rootAcl, TEXT_TURTLE);
|
||||
this.logger.debug(`Installing root ACL document at ${rootAcl.path}`);
|
||||
await this.store.setRepresentation(
|
||||
rootAcl,
|
||||
{
|
||||
binary: true,
|
||||
data: guardedStreamFrom([ acl ]),
|
||||
metadata,
|
||||
},
|
||||
);
|
||||
await this.store.setRepresentation(rootAcl, new BasicRepresentation(acl, rootAcl, TEXT_TURTLE));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { DataFactory } from 'n3';
|
||||
import { BasicRepresentation } from '../ldp/representation/BasicRepresentation';
|
||||
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
|
||||
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
@ -7,7 +8,6 @@ import { TEXT_TURTLE } from '../util/ContentTypes';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
import { ensureTrailingSlash } from '../util/PathUtil';
|
||||
import { generateResourceQuads } from '../util/ResourceUtil';
|
||||
import { guardedStreamFrom } from '../util/StreamUtil';
|
||||
import { PIM, RDF } from '../util/Vocabularies';
|
||||
import { Initializer } from './Initializer';
|
||||
import namedNode = DataFactory.namedNode;
|
||||
@ -54,20 +54,14 @@ export class RootContainerInitializer extends Initializer {
|
||||
* Create a root container in a ResourceStore.
|
||||
*/
|
||||
protected async createRootContainer(): Promise<void> {
|
||||
const metadata = new RepresentationMetadata(this.baseId);
|
||||
const metadata = new RepresentationMetadata(this.baseId, TEXT_TURTLE);
|
||||
metadata.addQuads(generateResourceQuads(namedNode(this.baseId.path), true));
|
||||
|
||||
// Make sure the root container is a pim:Storage
|
||||
// This prevents deletion of the root container as storage root containers can not be deleted
|
||||
metadata.add(RDF.type, PIM.terms.Storage);
|
||||
|
||||
metadata.contentType = TEXT_TURTLE;
|
||||
|
||||
this.logger.debug(`Creating root container at ${this.baseId.path}`);
|
||||
await this.store.setRepresentation(this.baseId, {
|
||||
binary: true,
|
||||
data: guardedStreamFrom([]),
|
||||
metadata,
|
||||
});
|
||||
await this.store.setRepresentation(this.baseId, new BasicRepresentation([], metadata));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||
import { BasicRepresentation } from '../representation/BasicRepresentation';
|
||||
import type { Representation } from '../representation/Representation';
|
||||
import type { BodyParserArgs } from './BodyParser';
|
||||
import { BodyParser } from './BodyParser';
|
||||
@ -27,10 +28,6 @@ export class RawBodyParser extends BodyParser {
|
||||
throw new BadRequestHttpError('HTTP request body was passed without Content-Type header');
|
||||
}
|
||||
|
||||
return {
|
||||
binary: true,
|
||||
data: request,
|
||||
metadata,
|
||||
};
|
||||
return new BasicRepresentation(request, metadata);
|
||||
}
|
||||
}
|
||||
|
113
src/ldp/representation/BasicRepresentation.ts
Normal file
113
src/ldp/representation/BasicRepresentation.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import type { Readable } from 'stream';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import type { Guarded } from '../../util/GuardedStream';
|
||||
import { guardStream } from '../../util/GuardedStream';
|
||||
import { guardedStreamFrom } from '../../util/StreamUtil';
|
||||
import type { Representation } from './Representation';
|
||||
import type { MetadataIdentifier, MetadataRecord } from './RepresentationMetadata';
|
||||
import { RepresentationMetadata, isRepresentationMetadata } from './RepresentationMetadata';
|
||||
|
||||
/**
|
||||
* Class with various constructors to facilitate creating a representation.
|
||||
*
|
||||
* A representation consists of 1) data, 2) metadata, and 3) a binary flag
|
||||
* to indicate whether the data is a binary stream or an object stream.
|
||||
*
|
||||
* 1. The data can be given as a stream, array, or string.
|
||||
* 2. The metadata can be specified as one or two parameters
|
||||
* that will be passed to the {@link RepresentationMetadata} constructor.
|
||||
* 3. The binary field is optional, and if not specified,
|
||||
* is determined from the content type inside the metadata.
|
||||
*/
|
||||
export class BasicRepresentation implements Representation {
|
||||
public readonly data: Guarded<Readable>;
|
||||
public readonly metadata: RepresentationMetadata;
|
||||
public readonly binary: boolean;
|
||||
|
||||
/**
|
||||
* @param data - The representation data
|
||||
* @param metadata - The representation metadata
|
||||
* @param binary - Whether the representation is a binary or object stream
|
||||
*/
|
||||
public constructor(
|
||||
data: Guarded<Readable> | Readable | any[] | string,
|
||||
metadata: RepresentationMetadata | MetadataRecord,
|
||||
binary?: boolean,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param data - The representation data
|
||||
* @param metadata - The representation metadata
|
||||
* @param contentType - The representation's content type
|
||||
* @param binary - Whether the representation is a binary or object stream
|
||||
*/
|
||||
public constructor(
|
||||
data: Guarded<Readable> | Readable | any[] | string,
|
||||
metadata: RepresentationMetadata | MetadataRecord,
|
||||
contentType?: string,
|
||||
binary?: boolean,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param data - The representation data
|
||||
* @param contentType - The representation's content type
|
||||
* @param binary - Whether the representation is a binary or object stream
|
||||
*/
|
||||
public constructor(
|
||||
data: Guarded<Readable> | Readable | any[] | string,
|
||||
contentType: string,
|
||||
binary?: boolean,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param data - The representation data
|
||||
* @param identifier - The representation's identifier
|
||||
* @param metadata - The representation metadata
|
||||
* @param binary - Whether the representation is a binary or object stream
|
||||
*/
|
||||
public constructor(
|
||||
data: Guarded<Readable> | Readable | any[] | string,
|
||||
identifier: MetadataIdentifier,
|
||||
metadata?: MetadataRecord,
|
||||
binary?: boolean,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param data - The representation data
|
||||
* @param identifier - The representation's identifier
|
||||
* @param contentType - The representation's content type
|
||||
* @param binary - Whether the representation is a binary or object stream
|
||||
*/
|
||||
public constructor(
|
||||
data: Guarded<Readable> | Readable | any[] | string,
|
||||
identifier: MetadataIdentifier,
|
||||
contentType?: string,
|
||||
binary?: boolean,
|
||||
);
|
||||
|
||||
public constructor(
|
||||
data: Readable | any[] | string,
|
||||
metadata: RepresentationMetadata | MetadataRecord | MetadataIdentifier | string,
|
||||
metadataRest?: MetadataRecord | string | boolean,
|
||||
binary?: boolean,
|
||||
) {
|
||||
if (typeof data === 'string' || Array.isArray(data)) {
|
||||
data = guardedStreamFrom(data);
|
||||
}
|
||||
this.data = guardStream(data);
|
||||
|
||||
if (typeof metadataRest === 'boolean') {
|
||||
binary = metadataRest;
|
||||
metadataRest = undefined;
|
||||
}
|
||||
if (!isRepresentationMetadata(metadata) || typeof metadataRest === 'string') {
|
||||
metadata = new RepresentationMetadata(metadata as any, metadataRest as any);
|
||||
}
|
||||
this.metadata = metadata;
|
||||
|
||||
if (typeof binary !== 'boolean') {
|
||||
binary = metadata.contentType !== INTERNAL_QUADS;
|
||||
}
|
||||
this.binary = binary;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { Parser } from 'n3';
|
||||
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
|
||||
import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata';
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import type {
|
||||
@ -8,7 +9,6 @@ import type {
|
||||
ResourceLink,
|
||||
} from '../../storage/mapping/FileIdentifierMapper';
|
||||
import { joinFilePath, isContainerIdentifier } from '../../util/PathUtil';
|
||||
import { guardedStreamFrom } from '../../util/StreamUtil';
|
||||
import type { Resource, ResourcesGenerator } from './ResourcesGenerator';
|
||||
import type { TemplateEngine } from './TemplateEngine';
|
||||
import Dict = NodeJS.Dict;
|
||||
@ -123,11 +123,7 @@ export class TemplatedResourcesGenerator implements ResourcesGenerator {
|
||||
|
||||
return {
|
||||
identifier: link.identifier,
|
||||
representation: {
|
||||
binary: true,
|
||||
data: guardedStreamFrom(data),
|
||||
metadata,
|
||||
},
|
||||
representation: new BasicRepresentation(data, metadata),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,9 @@ import arrayifyStream from 'arrayify-stream';
|
||||
import { DataFactory } from 'n3';
|
||||
import type { Quad, Term } from 'rdf-js';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { BasicRepresentation } from '../ldp/representation/BasicRepresentation';
|
||||
import type { Representation } from '../ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
|
||||
import type { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
|
||||
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
|
||||
import { INTERNAL_QUADS } from '../util/ContentTypes';
|
||||
import { BadRequestHttpError } from '../util/errors/BadRequestHttpError';
|
||||
@ -21,7 +22,6 @@ import {
|
||||
} from '../util/PathUtil';
|
||||
import { parseQuads } from '../util/QuadUtil';
|
||||
import { generateResourceQuads } from '../util/ResourceUtil';
|
||||
import { guardedStreamFrom } from '../util/StreamUtil';
|
||||
import { CONTENT_TYPE, HTTP, LDP, PIM, RDF } from '../util/Vocabularies';
|
||||
import type { DataAccessor } from './accessors/DataAccessor';
|
||||
import type { ResourceStore } from './ResourceStore';
|
||||
@ -64,28 +64,9 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
// In the future we want to use getNormalizedMetadata and redirect in case the identifier differs
|
||||
const metadata = await this.accessor.getMetadata(identifier);
|
||||
|
||||
let result: Representation;
|
||||
|
||||
// Create the representation of a container
|
||||
if (this.isExistingContainer(metadata)) {
|
||||
// Generate the data stream before setting the content-type to prevent unnecessary triples
|
||||
const data = guardedStreamFrom(metadata.quads());
|
||||
metadata.contentType = INTERNAL_QUADS;
|
||||
result = {
|
||||
binary: false,
|
||||
data,
|
||||
metadata,
|
||||
};
|
||||
|
||||
// Obtain a representation of a document
|
||||
} else {
|
||||
result = {
|
||||
binary: metadata.contentType !== INTERNAL_QUADS,
|
||||
data: await this.accessor.getData(identifier),
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
return this.isExistingContainer(metadata) ?
|
||||
new BasicRepresentation(metadata.quads(), metadata, INTERNAL_QUADS) :
|
||||
new BasicRepresentation(await this.accessor.getData(identifier), metadata);
|
||||
}
|
||||
|
||||
public async addResource(container: ResourceIdentifier, representation: Representation): Promise<ResourceIdentifier> {
|
||||
@ -374,22 +355,10 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
if (error instanceof NotFoundHttpError) {
|
||||
// Make sure the parent exists first
|
||||
await this.createRecursiveContainers(this.identifierStrategy.getParentContainer(container));
|
||||
await this.writeData(container, this.getEmptyContainerRepresentation(container), true);
|
||||
await this.writeData(container, new BasicRepresentation([], container), true);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the minimal representation for an empty container.
|
||||
* @param container - Identifier of this new container.
|
||||
*/
|
||||
protected getEmptyContainerRepresentation(container: ResourceIdentifier): Representation {
|
||||
return {
|
||||
binary: true,
|
||||
data: guardedStreamFrom([]),
|
||||
metadata: new RepresentationMetadata(container),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import type { Readable } from 'stream';
|
||||
import { StreamWriter } from 'n3';
|
||||
import rdfSerializer from 'rdf-serialize';
|
||||
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
|
||||
import type { Representation } from '../../ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata';
|
||||
import type { ValuePreferences } from '../../ldp/representation/RepresentationPreferences';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { guardStream } from '../../util/GuardedStream';
|
||||
import { pipeSafely } from '../../util/StreamUtil';
|
||||
import { PREFERRED_PREFIX_TERM } from '../../util/Vocabularies';
|
||||
import { matchingMediaTypes } from './ConversionUtil';
|
||||
@ -27,22 +26,18 @@ export class QuadToRdfConverter extends TypedRepresentationConverter {
|
||||
|
||||
public async handle({ representation: quads, preferences }: RepresentationConverterArgs): Promise<Representation> {
|
||||
const contentType = matchingMediaTypes(preferences.type, await this.getOutputTypes())[0];
|
||||
const metadata = new RepresentationMetadata(quads.metadata, contentType);
|
||||
let data: Readable;
|
||||
|
||||
// Use prefixes if possible (see https://github.com/rubensworks/rdf-serialize.js/issues/1)
|
||||
if (/(?:turtle|trig)$/u.test(contentType)) {
|
||||
const prefixes = Object.fromEntries(metadata.quads(null, PREFERRED_PREFIX_TERM, null)
|
||||
const prefixes = Object.fromEntries(quads.metadata.quads(null, PREFERRED_PREFIX_TERM, null)
|
||||
.map(({ subject, object }): [string, string] => [ object.value, subject.value ]));
|
||||
data = pipeSafely(quads.data, new StreamWriter({ format: contentType, prefixes }));
|
||||
// Otherwise, write without prefixes
|
||||
} else {
|
||||
data = rdfSerializer.serialize(quads.data, { contentType }) as Readable;
|
||||
}
|
||||
return {
|
||||
binary: true,
|
||||
data: guardStream(data),
|
||||
metadata,
|
||||
};
|
||||
|
||||
return new BasicRepresentation(data, quads.metadata, contentType);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { PassThrough } from 'stream';
|
||||
import rdfParser from 'rdf-parse';
|
||||
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
|
||||
import type { Representation } from '../../ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||
import { pipeSafely } from '../../util/StreamUtil';
|
||||
@ -17,19 +17,13 @@ export class RdfToQuadConverter extends TypedRepresentationConverter {
|
||||
}
|
||||
|
||||
public async handle({ representation, identifier }: RepresentationConverterArgs): Promise<Representation> {
|
||||
const metadata = new RepresentationMetadata(representation.metadata, INTERNAL_QUADS);
|
||||
const rawQuads = rdfParser.parse(representation.data, {
|
||||
contentType: representation.metadata.contentType!,
|
||||
baseIRI: identifier.path,
|
||||
});
|
||||
|
||||
const pass = new PassThrough({ objectMode: true });
|
||||
const data = pipeSafely(rawQuads, pass, (error): Error => new BadRequestHttpError(error.message));
|
||||
|
||||
return {
|
||||
binary: false,
|
||||
data,
|
||||
metadata,
|
||||
};
|
||||
return new BasicRepresentation(data, representation.metadata, INTERNAL_QUADS);
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,12 @@ import type { BaseQuad } from 'rdf-js';
|
||||
import { someTerms } from 'rdf-terms';
|
||||
import { Algebra } from 'sparqlalgebrajs';
|
||||
import type { SparqlUpdatePatch } from '../../ldp/http/SparqlUpdatePatch';
|
||||
import type { Representation } from '../../ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata';
|
||||
import { BasicRepresentation } from '../../ldp/representation/BasicRepresentation';
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import { guardStream } from '../../util/GuardedStream';
|
||||
import type { ResourceLocker } from '../../util/locking/ResourceLocker';
|
||||
import type { ResourceStore } from '../ResourceStore';
|
||||
import { PatchHandler } from './PatchHandler';
|
||||
@ -108,12 +106,6 @@ export class SparqlUpdatePatchHandler extends PatchHandler {
|
||||
this.logger.debug(`${store.size} quads will be stored to ${identifier.path}.`);
|
||||
|
||||
// Write the result
|
||||
const metadata = new RepresentationMetadata(identifier, INTERNAL_QUADS);
|
||||
const representation: Representation = {
|
||||
binary: false,
|
||||
data: guardStream(store.match() as Readable),
|
||||
metadata,
|
||||
};
|
||||
await this.source.setRepresentation(identifier, representation);
|
||||
await this.source.setRepresentation(identifier, new BasicRepresentation(store.match() as Readable, INTERNAL_QUADS));
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export type Guarded<T extends NodeJS.EventEmitter = NodeJS.EventEmitter> = T & G
|
||||
* Determines whether the stream is guarded from emitting errors.
|
||||
*/
|
||||
export function isGuarded<T extends NodeJS.EventEmitter>(stream: T): stream is Guarded<T> {
|
||||
return guardedErrors in stream;
|
||||
return typeof (stream as any)[guardedErrors] === 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,10 +97,10 @@ export function transformSafely<T = any>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an iterable to a stream and applies an error guard so that it is {@link Guarded}.
|
||||
* @param iterable - Data to stream.
|
||||
* Converts a string or array to a stream and applies an error guard so that it is {@link Guarded}.
|
||||
* @param contents - Data to stream.
|
||||
* @param options - Options to pass to the Readable constructor. See {@link Readable.from}.
|
||||
*/
|
||||
export function guardedStreamFrom(iterable: Iterable<any>, options?: ReadableOptions): Guarded<Readable> {
|
||||
return guardStream(Readable.from(iterable, options));
|
||||
export function guardedStreamFrom(contents: string | Iterable<any>, options?: ReadableOptions): Guarded<Readable> {
|
||||
return guardStream(Readable.from(typeof contents === 'string' ? [ contents ] : contents, options));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createReadStream } from 'fs';
|
||||
import type { HttpHandler, Initializer, ResourceStore } from '../../src/';
|
||||
import { LDP, RepresentationMetadata, guardStream, joinFilePath } from '../../src/';
|
||||
import { LDP, BasicRepresentation, joinFilePath } from '../../src/';
|
||||
import { AclHelper, ResourceHelper } from '../util/TestHelpers';
|
||||
import { BASE, getTestFolder, createFolder, removeFolder, instantiateFromConfig } from './Config';
|
||||
|
||||
@ -53,11 +53,8 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeUrn, se
|
||||
resourceHelper = new ResourceHelper(handler, BASE);
|
||||
|
||||
// Write test resource
|
||||
await store.setRepresentation({ path: `${BASE}/permanent.txt` }, {
|
||||
binary: true,
|
||||
data: guardStream(createReadStream(joinFilePath(__dirname, '../assets/permanent.txt'))),
|
||||
metadata: new RepresentationMetadata('text/plain'),
|
||||
});
|
||||
await store.setRepresentation({ path: `${BASE}/permanent.txt` },
|
||||
new BasicRepresentation(createReadStream(joinFilePath(__dirname, '../assets/permanent.txt')), 'text/plain'));
|
||||
});
|
||||
|
||||
afterAll(async(): Promise<void> => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import streamifyArray from 'streamify-array';
|
||||
import { RootContainerInitializer } from '../../src/init/RootContainerInitializer';
|
||||
import { BasicRepresentation } from '../../src/ldp/representation/BasicRepresentation';
|
||||
import type { Representation } from '../../src/ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
|
||||
import { InMemoryDataAccessor } from '../../src/storage/accessors/InMemoryDataAccessor';
|
||||
import { DataAccessorBasedStore } from '../../src/storage/DataAccessorBasedStore';
|
||||
import { LockingResourceStore } from '../../src/storage/LockingResourceStore';
|
||||
@ -12,7 +12,6 @@ import type { ExpiringResourceLocker } from '../../src/util/locking/ExpiringReso
|
||||
import type { ResourceLocker } from '../../src/util/locking/ResourceLocker';
|
||||
import { SingleThreadedResourceLocker } from '../../src/util/locking/SingleThreadedResourceLocker';
|
||||
import { WrappedExpiringResourceLocker } from '../../src/util/locking/WrappedExpiringResourceLocker';
|
||||
import { guardedStreamFrom } from '../../src/util/StreamUtil';
|
||||
import { BASE } from './Config';
|
||||
|
||||
describe('A LockingResourceStore', (): void => {
|
||||
@ -39,9 +38,7 @@ describe('A LockingResourceStore', (): void => {
|
||||
store = new LockingResourceStore(source, expiringLocker);
|
||||
|
||||
// Make sure something is in the store before we read from it in our tests.
|
||||
const metadata = new RepresentationMetadata(APPLICATION_OCTET_STREAM);
|
||||
const data = guardedStreamFrom([ 1, 2, 3 ]);
|
||||
await store.setRepresentation({ path }, { metadata, data, binary: true });
|
||||
await store.setRepresentation({ path }, new BasicRepresentation([ 1, 2, 3 ], APPLICATION_OCTET_STREAM));
|
||||
});
|
||||
|
||||
it('destroys the stream when nothing is read after 1000ms.', async(): Promise<void> => {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type { Representation } from '../../src/ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../src/ldp/representation/RepresentationMetadata';
|
||||
import { BasicRepresentation } from '../../src/ldp/representation/BasicRepresentation';
|
||||
import { ChainedConverter } from '../../src/storage/conversion/ChainedConverter';
|
||||
import { QuadToRdfConverter } from '../../src/storage/conversion/QuadToRdfConverter';
|
||||
import { RdfToQuadConverter } from '../../src/storage/conversion/RdfToQuadConverter';
|
||||
import { guardedStreamFrom, readableToString } from '../../src/util/StreamUtil';
|
||||
import { readableToString } from '../../src/util/StreamUtil';
|
||||
|
||||
describe('A ChainedConverter', (): void => {
|
||||
const converters = [
|
||||
@ -13,14 +12,10 @@ describe('A ChainedConverter', (): void => {
|
||||
const converter = new ChainedConverter(converters);
|
||||
|
||||
it('can convert from JSON-LD to turtle.', async(): Promise<void> => {
|
||||
const metadata = new RepresentationMetadata('application/ld+json');
|
||||
const representation: Representation = {
|
||||
binary: true,
|
||||
data: guardedStreamFrom(
|
||||
[ '{"@id": "http://test.com/s", "http://test.com/p": { "@id": "http://test.com/o" }}' ],
|
||||
),
|
||||
metadata,
|
||||
};
|
||||
const representation = new BasicRepresentation(
|
||||
'{"@id": "http://test.com/s", "http://test.com/p": { "@id": "http://test.com/o" }}',
|
||||
'application/ld+json',
|
||||
);
|
||||
|
||||
const result = await converter.handleSafe({
|
||||
representation,
|
||||
@ -33,12 +28,10 @@ describe('A ChainedConverter', (): void => {
|
||||
});
|
||||
|
||||
it('can convert from turtle to JSON-LD.', async(): Promise<void> => {
|
||||
const metadata = new RepresentationMetadata('text/turtle');
|
||||
const representation: Representation = {
|
||||
binary: true,
|
||||
data: guardedStreamFrom([ '<http://test.com/s> <http://test.com/p> <http://test.com/o>.' ]),
|
||||
metadata,
|
||||
};
|
||||
const representation = new BasicRepresentation(
|
||||
'<http://test.com/s> <http://test.com/p> <http://test.com/o>.',
|
||||
'text/turtle',
|
||||
);
|
||||
|
||||
const result = await converter.handleSafe({
|
||||
representation,
|
||||
|
118
test/unit/ldp/representation/BasicRepresentation.test.ts
Normal file
118
test/unit/ldp/representation/BasicRepresentation.test.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import 'jest-rdf';
|
||||
import { namedNode } from '@rdfjs/data-model';
|
||||
import arrayifyStream from 'arrayify-stream';
|
||||
import streamifyArray from 'streamify-array';
|
||||
import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation';
|
||||
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
|
||||
import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes';
|
||||
import { guardedStreamFrom } from '../../../../src/util/StreamUtil';
|
||||
import { CONTENT_TYPE } from '../../../../src/util/Vocabularies';
|
||||
|
||||
describe('BasicRepresentation', (): void => {
|
||||
it('creates a representation with (data, metadata, binary).', (): void => {
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const metadata = new RepresentationMetadata();
|
||||
const representation = new BasicRepresentation(data, metadata, true);
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, metadata).', (): void => {
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
let metadata = new RepresentationMetadata();
|
||||
let representation = new BasicRepresentation(data, metadata);
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(true);
|
||||
|
||||
metadata = new RepresentationMetadata(INTERNAL_QUADS);
|
||||
representation = new BasicRepresentation(data, metadata);
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(false);
|
||||
});
|
||||
|
||||
it('creates a representation with (unguarded data, metadata).', (): void => {
|
||||
const data = streamifyArray([ '' ]);
|
||||
const metadata = new RepresentationMetadata();
|
||||
const representation = new BasicRepresentation(data, metadata);
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (array data, metadata).', async(): Promise<void> => {
|
||||
const data = [ 'my', 'data' ];
|
||||
const metadata = new RepresentationMetadata();
|
||||
const representation = new BasicRepresentation(data, metadata);
|
||||
expect(await arrayifyStream(representation.data)).toEqual(data);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (string data, metadata).', async(): Promise<void> => {
|
||||
const data = 'my data';
|
||||
const metadata = new RepresentationMetadata();
|
||||
const representation = new BasicRepresentation(data, metadata);
|
||||
expect(await arrayifyStream(representation.data)).toEqual([ data ]);
|
||||
expect(representation.metadata).toBe(metadata);
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, metadata record).', (): void => {
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, { [CONTENT_TYPE]: 'text/custom' });
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, content type).', (): void => {
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, 'text/custom');
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, identifier, metadata record).', (): void => {
|
||||
const identifier = { path: 'http://example.org/#' };
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, identifier, { [CONTENT_TYPE]: 'text/custom' });
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.identifier).toEqualRdfTerm(namedNode(identifier.path));
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, identifier, content type).', (): void => {
|
||||
const identifier = { path: 'http://example.org/#' };
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, identifier, 'text/custom');
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.identifier).toEqualRdfTerm(namedNode(identifier.path));
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, identifier term, metadata record).', (): void => {
|
||||
const identifier = namedNode('http://example.org/#');
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, identifier, { [CONTENT_TYPE]: 'text/custom' });
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.identifier).toBe(identifier);
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
|
||||
it('creates a representation with (data, identifier term, content type).', (): void => {
|
||||
const identifier = namedNode('http://example.org/#');
|
||||
const data = guardedStreamFrom([ '' ]);
|
||||
const representation = new BasicRepresentation(data, identifier, 'text/custom');
|
||||
expect(representation.data).toBe(data);
|
||||
expect(representation.metadata.identifier).toBe(identifier);
|
||||
expect(representation.metadata.contentType).toBe('text/custom');
|
||||
expect(representation.binary).toBe(true);
|
||||
});
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
import { Readable } from 'stream';
|
||||
import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
|
||||
import { BasicRepresentation } from '../../../src/ldp/representation/BasicRepresentation';
|
||||
import type { ResourceIdentifier } from '../../../src/ldp/representation/ResourceIdentifier';
|
||||
import type { Agent } from '../../../src/pods/agent/Agent';
|
||||
import type { IdentifierGenerator } from '../../../src/pods/generate/IdentifierGenerator';
|
||||
@ -46,11 +45,7 @@ describe('A GeneratedPodManager', (): void => {
|
||||
});
|
||||
|
||||
it('throws an error if the generate identifier is not available.', async(): Promise<void> => {
|
||||
(store.getRepresentation as jest.Mock).mockImplementationOnce((): any => ({
|
||||
data: Readable.from([]),
|
||||
metadata: new RepresentationMetadata(),
|
||||
binary: true,
|
||||
}));
|
||||
(store.getRepresentation as jest.Mock).mockImplementationOnce((): any => new BasicRepresentation([], {}));
|
||||
const result = manager.createPod(agent);
|
||||
await expect(result).rejects.toThrow(`There already is a resource at ${base}user/`);
|
||||
await expect(result).rejects.toThrow(ConflictHttpError);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation';
|
||||
import type { Representation } from '../../../../src/ldp/representation/Representation';
|
||||
import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata';
|
||||
import { AgentJsonParser } from '../../../../src/pods/agent/AgentJsonParser';
|
||||
@ -11,13 +12,8 @@ describe('An AgentJsonParser', (): void => {
|
||||
const parser = new AgentJsonParser();
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
metadata = new RepresentationMetadata();
|
||||
metadata.contentType = 'application/json';
|
||||
representation = {
|
||||
binary: true,
|
||||
data: guardedStreamFrom([]),
|
||||
metadata,
|
||||
};
|
||||
metadata = new RepresentationMetadata('application/json');
|
||||
representation = new BasicRepresentation([], metadata);
|
||||
});
|
||||
|
||||
it('only supports JSON data.', async(): Promise<void> => {
|
||||
|
@ -6,7 +6,7 @@ import * as url from 'url';
|
||||
import type { MockResponse } from 'node-mocks-http';
|
||||
import { createResponse } from 'node-mocks-http';
|
||||
import type { ResourceStore, PermissionSet, HttpHandler, HttpRequest } from '../../src/';
|
||||
import { guardedStreamFrom, RepresentationMetadata, joinFilePath, ensureTrailingSlash } from '../../src/';
|
||||
import { BasicRepresentation, joinFilePath, ensureTrailingSlash } from '../../src/';
|
||||
import { performRequest } from './Util';
|
||||
|
||||
/* eslint-disable jest/no-standalone-expect */
|
||||
@ -45,16 +45,7 @@ export class AclHelper {
|
||||
|
||||
acl.push('.');
|
||||
|
||||
const representation = {
|
||||
binary: true,
|
||||
data: guardedStreamFrom(acl),
|
||||
metadata: new RepresentationMetadata('text/turtle'),
|
||||
};
|
||||
|
||||
return this.store.setRepresentation(
|
||||
{ path: `${this.id}.acl` },
|
||||
representation,
|
||||
);
|
||||
await this.store.setRepresentation({ path: `${this.id}.acl` }, new BasicRepresentation(acl, 'text/turtle'));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user