mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Only accept NamedNodes as predicates for metadata
* refactor: move toCachedNamedNode (private) * chore: only NamedNodes predicates in removes * feat: enforce NamedNode predicates in most cases * feat: getAll only accepts NamedNodes * feat: toCachedNamedNode only accepts string arg * tests: use NamedNodes for getAll calls * test: remove unnecessary string check for coverage * tests: fix NamedNodes in new tests after rebase * feat: metadatawriters store NamedNodes * refactor: toCachedNamedNode as utility function * fix: double write of linkRelMap * test: use the CONTENT_TYPE constant
This commit is contained in:
parent
db906ae872
commit
668d0a331f
@ -1,4 +1,5 @@
|
|||||||
import { DataFactory } from 'n3';
|
import { DataFactory } from 'n3';
|
||||||
|
import type { NamedNode } from 'rdf-js';
|
||||||
import { SOLID_META } from '../../util/Vocabularies';
|
import { SOLID_META } from '../../util/Vocabularies';
|
||||||
import type { RepresentationMetadata } from '../representation/RepresentationMetadata';
|
import type { RepresentationMetadata } from '../representation/RepresentationMetadata';
|
||||||
import type { AuxiliaryIdentifierStrategy } from './AuxiliaryIdentifierStrategy';
|
import type { AuxiliaryIdentifierStrategy } from './AuxiliaryIdentifierStrategy';
|
||||||
@ -11,12 +12,12 @@ import { MetadataGenerator } from './MetadataGenerator';
|
|||||||
* In case the input is metadata of an auxiliary resource no metadata will be added
|
* In case the input is metadata of an auxiliary resource no metadata will be added
|
||||||
*/
|
*/
|
||||||
export class LinkMetadataGenerator extends MetadataGenerator {
|
export class LinkMetadataGenerator extends MetadataGenerator {
|
||||||
private readonly link: string;
|
private readonly link: NamedNode;
|
||||||
private readonly identifierStrategy: AuxiliaryIdentifierStrategy;
|
private readonly identifierStrategy: AuxiliaryIdentifierStrategy;
|
||||||
|
|
||||||
public constructor(link: string, identifierStrategy: AuxiliaryIdentifierStrategy) {
|
public constructor(link: string, identifierStrategy: AuxiliaryIdentifierStrategy) {
|
||||||
super();
|
super();
|
||||||
this.link = link;
|
this.link = DataFactory.namedNode(link);
|
||||||
this.identifierStrategy = identifierStrategy;
|
this.identifierStrategy = identifierStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export class SlugParser extends MetadataParser {
|
|||||||
throw new BadRequestHttpError('Request has multiple Slug headers');
|
throw new BadRequestHttpError('Request has multiple Slug headers');
|
||||||
}
|
}
|
||||||
this.logger.debug(`Request Slug is '${slug}'.`);
|
this.logger.debug(`Request Slug is '${slug}'.`);
|
||||||
input.metadata.set(SOLID_HTTP.slug, slug);
|
input.metadata.set(SOLID_HTTP.terms.slug, slug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import type { NamedNode } from 'n3';
|
||||||
|
import { DataFactory } from 'n3';
|
||||||
import { getLoggerFor } from '../../../logging/LogUtil';
|
import { getLoggerFor } from '../../../logging/LogUtil';
|
||||||
import type { HttpResponse } from '../../../server/HttpResponse';
|
import type { HttpResponse } from '../../../server/HttpResponse';
|
||||||
import { addHeader } from '../../../util/HeaderUtil';
|
import { addHeader } from '../../../util/HeaderUtil';
|
||||||
@ -9,19 +11,23 @@ import { MetadataWriter } from './MetadataWriter';
|
|||||||
* The values of the objects will be put in a Link header with the corresponding "rel" value.
|
* The values of the objects will be put in a Link header with the corresponding "rel" value.
|
||||||
*/
|
*/
|
||||||
export class LinkRelMetadataWriter extends MetadataWriter {
|
export class LinkRelMetadataWriter extends MetadataWriter {
|
||||||
private readonly linkRelMap: Record<string, string>;
|
private readonly linkRelMap: Map<NamedNode, string>;
|
||||||
protected readonly logger = getLoggerFor(this);
|
protected readonly logger = getLoggerFor(this);
|
||||||
|
|
||||||
public constructor(linkRelMap: Record<string, string>) {
|
public constructor(linkRelMap: Record<string, string>) {
|
||||||
super();
|
super();
|
||||||
this.linkRelMap = linkRelMap;
|
|
||||||
|
this.linkRelMap = new Map<NamedNode, string>();
|
||||||
|
for (const [ key, value ] of Object.entries(linkRelMap)) {
|
||||||
|
this.linkRelMap.set(DataFactory.namedNode(key), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
|
public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
|
||||||
const keys = Object.keys(this.linkRelMap);
|
this.logger.debug(`Available link relations: ${this.linkRelMap.size}`);
|
||||||
this.logger.debug(`Available link relations: ${keys.length}`);
|
for (const [ predicate, relValue ] of this.linkRelMap) {
|
||||||
for (const key of keys) {
|
const values = input.metadata.getAll(predicate)
|
||||||
const values = input.metadata.getAll(key).map((term): string => `<${term.value}>; rel="${this.linkRelMap[key]}"`);
|
.map((term): string => `<${term.value}>; rel="${relValue}"`);
|
||||||
if (values.length > 0) {
|
if (values.length > 0) {
|
||||||
this.logger.debug(`Adding Link header ${values}`);
|
this.logger.debug(`Adding Link header ${values}`);
|
||||||
addHeader(input.response, 'Link', values);
|
addHeader(input.response, 'Link', values);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import type { NamedNode } from 'n3';
|
||||||
|
import { DataFactory } from 'n3';
|
||||||
import type { HttpResponse } from '../../../server/HttpResponse';
|
import type { HttpResponse } from '../../../server/HttpResponse';
|
||||||
import { addHeader } from '../../../util/HeaderUtil';
|
import { addHeader } from '../../../util/HeaderUtil';
|
||||||
import type { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
import type { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
||||||
@ -8,11 +10,15 @@ import { MetadataWriter } from './MetadataWriter';
|
|||||||
* The header value(s) will be the same as the corresponding object value(s).
|
* The header value(s) will be the same as the corresponding object value(s).
|
||||||
*/
|
*/
|
||||||
export class MappedMetadataWriter extends MetadataWriter {
|
export class MappedMetadataWriter extends MetadataWriter {
|
||||||
private readonly headerMap: [string, string][];
|
private readonly headerMap: Map<NamedNode, string>;
|
||||||
|
|
||||||
public constructor(headerMap: Record<string, string>) {
|
public constructor(headerMap: Record<string, string>) {
|
||||||
super();
|
super();
|
||||||
this.headerMap = Object.entries(headerMap);
|
|
||||||
|
this.headerMap = new Map<NamedNode, string>();
|
||||||
|
for (const [ key, value ] of Object.entries(headerMap)) {
|
||||||
|
this.headerMap.set(DataFactory.namedNode(key), value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
|
public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise<void> {
|
||||||
|
@ -4,7 +4,7 @@ import { getLoggerFor } from '../../logging/LogUtil';
|
|||||||
import { InternalServerError } from '../../util/errors/InternalServerError';
|
import { InternalServerError } from '../../util/errors/InternalServerError';
|
||||||
import type { ContentType } from '../../util/HeaderUtil';
|
import type { ContentType } from '../../util/HeaderUtil';
|
||||||
import { parseContentType } from '../../util/HeaderUtil';
|
import { parseContentType } from '../../util/HeaderUtil';
|
||||||
import { toNamedTerm, toObjectTerm, toCachedNamedNode, isTerm, toLiteral } from '../../util/TermUtil';
|
import { toNamedTerm, toObjectTerm, isTerm, toLiteral } from '../../util/TermUtil';
|
||||||
import { CONTENT_TYPE_TERM, CONTENT_LENGTH_TERM, XSD, SOLID_META, RDFS } from '../../util/Vocabularies';
|
import { CONTENT_TYPE_TERM, CONTENT_LENGTH_TERM, XSD, SOLID_META, RDFS } from '../../util/Vocabularies';
|
||||||
import type { ResourceIdentifier } from './ResourceIdentifier';
|
import type { ResourceIdentifier } from './ResourceIdentifier';
|
||||||
import { isResourceIdentifier } from './ResourceIdentifier';
|
import { isResourceIdentifier } from './ResourceIdentifier';
|
||||||
@ -21,6 +21,22 @@ export function isRepresentationMetadata(object: any): object is RepresentationM
|
|||||||
return typeof object?.setMetadata === 'function';
|
return typeof object?.setMetadata === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caches named node conversions
|
||||||
|
const cachedNamedNodes: Record<string, NamedNode> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the incoming name (URI or shorthand) to a named node.
|
||||||
|
* The generated terms get cached to reduce the number of created nodes,
|
||||||
|
* so only use this for internal constants!
|
||||||
|
* @param name - Predicate to potentially transform.
|
||||||
|
*/
|
||||||
|
function toCachedNamedNode(name: string): NamedNode {
|
||||||
|
if (!(name in cachedNamedNodes)) {
|
||||||
|
cachedNamedNodes[name] = DataFactory.namedNode(name);
|
||||||
|
}
|
||||||
|
return cachedNamedNodes[name];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the metadata triples and provides methods for easy access.
|
* Stores the metadata triples and provides methods for easy access.
|
||||||
* Most functions return the metadata object to allow for chaining.
|
* Most functions return the metadata object to allow for chaining.
|
||||||
@ -116,7 +132,7 @@ export class RepresentationMetadata {
|
|||||||
*/
|
*/
|
||||||
public quads(
|
public quads(
|
||||||
subject: NamedNode | BlankNode | string | null = null,
|
subject: NamedNode | BlankNode | string | null = null,
|
||||||
predicate: NamedNode | string | null = null,
|
predicate: NamedNode | null = null,
|
||||||
object: NamedNode | BlankNode | Literal | string | null = null,
|
object: NamedNode | BlankNode | Literal | string | null = null,
|
||||||
graph: MetadataGraph | null = null,
|
graph: MetadataGraph | null = null,
|
||||||
): Quad[] {
|
): Quad[] {
|
||||||
@ -167,12 +183,12 @@ export class RepresentationMetadata {
|
|||||||
*/
|
*/
|
||||||
public addQuad(
|
public addQuad(
|
||||||
subject: NamedNode | BlankNode | string,
|
subject: NamedNode | BlankNode | string,
|
||||||
predicate: NamedNode | string,
|
predicate: NamedNode,
|
||||||
object: NamedNode | BlankNode | Literal | string,
|
object: NamedNode | BlankNode | Literal | string,
|
||||||
graph?: MetadataGraph,
|
graph?: MetadataGraph,
|
||||||
): this {
|
): this {
|
||||||
this.store.addQuad(toNamedTerm(subject),
|
this.store.addQuad(toNamedTerm(subject),
|
||||||
toCachedNamedNode(predicate),
|
predicate,
|
||||||
toObjectTerm(object, true),
|
toObjectTerm(object, true),
|
||||||
graph ? toNamedTerm(graph) : undefined);
|
graph ? toNamedTerm(graph) : undefined);
|
||||||
return this;
|
return this;
|
||||||
@ -194,12 +210,12 @@ export class RepresentationMetadata {
|
|||||||
*/
|
*/
|
||||||
public removeQuad(
|
public removeQuad(
|
||||||
subject: NamedNode | BlankNode | string,
|
subject: NamedNode | BlankNode | string,
|
||||||
predicate: NamedNode | string,
|
predicate: NamedNode,
|
||||||
object: NamedNode | BlankNode | Literal | string,
|
object: NamedNode | BlankNode | Literal | string,
|
||||||
graph?: MetadataGraph,
|
graph?: MetadataGraph,
|
||||||
): this {
|
): this {
|
||||||
const quads = this.quads(toNamedTerm(subject),
|
const quads = this.quads(toNamedTerm(subject),
|
||||||
toCachedNamedNode(predicate),
|
predicate,
|
||||||
toObjectTerm(object, true),
|
toObjectTerm(object, true),
|
||||||
graph ? toNamedTerm(graph) : undefined);
|
graph ? toNamedTerm(graph) : undefined);
|
||||||
return this.removeQuads(quads);
|
return this.removeQuads(quads);
|
||||||
@ -219,7 +235,7 @@ export class RepresentationMetadata {
|
|||||||
* @param object - Value(s) to add.
|
* @param object - Value(s) to add.
|
||||||
* @param graph - Optional graph of where to add the values to.
|
* @param graph - Optional graph of where to add the values to.
|
||||||
*/
|
*/
|
||||||
public add(predicate: NamedNode | string, object: MetadataValue, graph?: MetadataGraph): this {
|
public add(predicate: NamedNode, object: MetadataValue, graph?: MetadataGraph): this {
|
||||||
return this.forQuads(predicate, object, (pred, obj): any => this.addQuad(this.id, pred, obj, graph));
|
return this.forQuads(predicate, object, (pred, obj): any => this.addQuad(this.id, pred, obj, graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +245,7 @@ export class RepresentationMetadata {
|
|||||||
* @param object - Value(s) to remove.
|
* @param object - Value(s) to remove.
|
||||||
* @param graph - Optional graph of where to remove the values from.
|
* @param graph - Optional graph of where to remove the values from.
|
||||||
*/
|
*/
|
||||||
public remove(predicate: NamedNode | string, object: MetadataValue, graph?: MetadataGraph): this {
|
public remove(predicate: NamedNode, object: MetadataValue, graph?: MetadataGraph): this {
|
||||||
return this.forQuads(predicate, object, (pred, obj): any => this.removeQuad(this.id, pred, obj, graph));
|
return this.forQuads(predicate, object, (pred, obj): any => this.removeQuad(this.id, pred, obj, graph));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,12 +253,11 @@ export class RepresentationMetadata {
|
|||||||
* Helper function to simplify add/remove
|
* Helper function to simplify add/remove
|
||||||
* Runs the given function on all predicate/object pairs, but only converts the predicate to a named node once.
|
* Runs the given function on all predicate/object pairs, but only converts the predicate to a named node once.
|
||||||
*/
|
*/
|
||||||
private forQuads(predicate: NamedNode | string, object: MetadataValue,
|
private forQuads(predicate: NamedNode, object: MetadataValue,
|
||||||
forFn: (pred: NamedNode, obj: NamedNode | Literal) => void): this {
|
forFn: (pred: NamedNode, obj: NamedNode | Literal) => void): this {
|
||||||
const predicateNode = toCachedNamedNode(predicate);
|
|
||||||
const objects = Array.isArray(object) ? object : [ object ];
|
const objects = Array.isArray(object) ? object : [ object ];
|
||||||
for (const obj of objects) {
|
for (const obj of objects) {
|
||||||
forFn(predicateNode, toObjectTerm(obj, true));
|
forFn(predicate, toObjectTerm(obj, true));
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -252,8 +267,8 @@ export class RepresentationMetadata {
|
|||||||
* @param predicate - Predicate to remove.
|
* @param predicate - Predicate to remove.
|
||||||
* @param graph - Optional graph where to remove from.
|
* @param graph - Optional graph where to remove from.
|
||||||
*/
|
*/
|
||||||
public removeAll(predicate: NamedNode | string, graph?: MetadataGraph): this {
|
public removeAll(predicate: NamedNode, graph?: MetadataGraph): this {
|
||||||
this.removeQuads(this.store.getQuads(this.id, toCachedNamedNode(predicate), null, graph ?? null));
|
this.removeQuads(this.store.getQuads(this.id, predicate, null, graph ?? null));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,8 +293,8 @@ export class RepresentationMetadata {
|
|||||||
*
|
*
|
||||||
* @returns An array with all matches.
|
* @returns An array with all matches.
|
||||||
*/
|
*/
|
||||||
public getAll(predicate: NamedNode | string, graph?: MetadataGraph): Term[] {
|
public getAll(predicate: NamedNode, graph?: MetadataGraph): Term[] {
|
||||||
return this.store.getQuads(this.id, toCachedNamedNode(predicate), null, graph ?? null)
|
return this.store.getQuads(this.id, predicate, null, graph ?? null)
|
||||||
.map((quad): Term => quad.object);
|
.map((quad): Term => quad.object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,15 +307,15 @@ export class RepresentationMetadata {
|
|||||||
*
|
*
|
||||||
* @returns The corresponding value. Undefined if there is no match
|
* @returns The corresponding value. Undefined if there is no match
|
||||||
*/
|
*/
|
||||||
public get(predicate: NamedNode | string, graph?: MetadataGraph): Term | undefined {
|
public get(predicate: NamedNode, graph?: MetadataGraph): Term | undefined {
|
||||||
const terms = this.getAll(predicate, graph);
|
const terms = this.getAll(predicate, graph);
|
||||||
if (terms.length === 0) {
|
if (terms.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (terms.length > 1) {
|
if (terms.length > 1) {
|
||||||
this.logger.error(`Multiple results for ${typeof predicate === 'string' ? predicate : predicate.value}`);
|
this.logger.error(`Multiple results for ${predicate.value}`);
|
||||||
throw new InternalServerError(
|
throw new InternalServerError(
|
||||||
`Multiple results for ${typeof predicate === 'string' ? predicate : predicate.value}`,
|
`Multiple results for ${predicate.value}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return terms[0];
|
return terms[0];
|
||||||
@ -313,7 +328,7 @@ export class RepresentationMetadata {
|
|||||||
* @param object - Value(s) to set.
|
* @param object - Value(s) to set.
|
||||||
* @param graph - Optional graph where the triple should be stored.
|
* @param graph - Optional graph where the triple should be stored.
|
||||||
*/
|
*/
|
||||||
public set(predicate: NamedNode | string, object?: MetadataValue, graph?: MetadataGraph): this {
|
public set(predicate: NamedNode, object?: MetadataValue, graph?: MetadataGraph): this {
|
||||||
this.removeAll(predicate, graph);
|
this.removeAll(predicate, graph);
|
||||||
if (object) {
|
if (object) {
|
||||||
this.add(predicate, object, graph);
|
this.add(predicate, object, graph);
|
||||||
|
@ -29,7 +29,6 @@ import {
|
|||||||
import { parseQuads } from '../util/QuadUtil';
|
import { parseQuads } from '../util/QuadUtil';
|
||||||
import { addResourceMetadata, updateModifiedDate } from '../util/ResourceUtil';
|
import { addResourceMetadata, updateModifiedDate } from '../util/ResourceUtil';
|
||||||
import {
|
import {
|
||||||
CONTENT_TYPE,
|
|
||||||
DC,
|
DC,
|
||||||
SOLID_HTTP,
|
SOLID_HTTP,
|
||||||
LDP,
|
LDP,
|
||||||
@ -39,6 +38,7 @@ import {
|
|||||||
XSD,
|
XSD,
|
||||||
SOLID_META,
|
SOLID_META,
|
||||||
PREFERRED_PREFIX_TERM,
|
PREFERRED_PREFIX_TERM,
|
||||||
|
CONTENT_TYPE_TERM,
|
||||||
} from '../util/Vocabularies';
|
} from '../util/Vocabularies';
|
||||||
import type { DataAccessor } from './accessors/DataAccessor';
|
import type { DataAccessor } from './accessors/DataAccessor';
|
||||||
import type { Conditions } from './Conditions';
|
import type { Conditions } from './Conditions';
|
||||||
@ -435,7 +435,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Input content type doesn't matter anymore
|
// Input content type doesn't matter anymore
|
||||||
representation.metadata.removeAll(CONTENT_TYPE);
|
representation.metadata.removeAll(CONTENT_TYPE_TERM);
|
||||||
|
|
||||||
// Container data is stored in the metadata
|
// Container data is stored in the metadata
|
||||||
representation.metadata.addQuads(quads);
|
representation.metadata.addQuads(quads);
|
||||||
@ -516,8 +516,8 @@ export class DataAccessorBasedStore implements ResourceStore {
|
|||||||
Promise<ResourceIdentifier> {
|
Promise<ResourceIdentifier> {
|
||||||
// Get all values needed for naming the resource
|
// Get all values needed for naming the resource
|
||||||
const isContainer = this.isContainerType(metadata);
|
const isContainer = this.isContainerType(metadata);
|
||||||
const slug = metadata.get(SOLID_HTTP.slug)?.value;
|
const slug = metadata.get(SOLID_HTTP.terms.slug)?.value;
|
||||||
metadata.removeAll(SOLID_HTTP.slug);
|
metadata.removeAll(SOLID_HTTP.terms.slug);
|
||||||
|
|
||||||
let newID: ResourceIdentifier = this.createURI(container, isContainer, slug);
|
let newID: ResourceIdentifier = this.createURI(container, isContainer, slug);
|
||||||
|
|
||||||
@ -544,7 +544,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
|||||||
* @param metadata - Metadata of the (new) resource.
|
* @param metadata - Metadata of the (new) resource.
|
||||||
*/
|
*/
|
||||||
protected isContainerType(metadata: RepresentationMetadata): boolean {
|
protected isContainerType(metadata: RepresentationMetadata): boolean {
|
||||||
return this.hasContainerType(metadata.getAll(RDF.type));
|
return this.hasContainerType(metadata.getAll(RDF.terms.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -558,7 +558,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
|||||||
* Verifies if this is the metadata of a root storage container.
|
* Verifies if this is the metadata of a root storage container.
|
||||||
*/
|
*/
|
||||||
protected isRootStorage(metadata: RepresentationMetadata): boolean {
|
protected isRootStorage(metadata: RepresentationMetadata): boolean {
|
||||||
return metadata.getAll(RDF.type).some((term): boolean => term.value === PIM.Storage);
|
return metadata.getAll(RDF.terms.type).some((term): boolean => term.value === PIM.Storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@ import { joinFilePath, isContainerIdentifier } from '../../util/PathUtil';
|
|||||||
import { parseQuads, serializeQuads } from '../../util/QuadUtil';
|
import { parseQuads, serializeQuads } from '../../util/QuadUtil';
|
||||||
import { addResourceMetadata, updateModifiedDate } from '../../util/ResourceUtil';
|
import { addResourceMetadata, updateModifiedDate } from '../../util/ResourceUtil';
|
||||||
import { toLiteral, toNamedTerm } from '../../util/TermUtil';
|
import { toLiteral, toNamedTerm } from '../../util/TermUtil';
|
||||||
import { CONTENT_TYPE, DC, IANA, LDP, POSIX, RDF, SOLID_META, XSD } from '../../util/Vocabularies';
|
import { CONTENT_TYPE_TERM, DC, IANA, LDP, POSIX, RDF, SOLID_META, XSD } from '../../util/Vocabularies';
|
||||||
import type { FileIdentifierMapper, ResourceLink } from '../mapping/FileIdentifierMapper';
|
import type { FileIdentifierMapper, ResourceLink } from '../mapping/FileIdentifierMapper';
|
||||||
import type { DataAccessor } from './DataAccessor';
|
import type { DataAccessor } from './DataAccessor';
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ export class FileDataAccessor implements DataAccessor {
|
|||||||
private async getFileMetadata(link: ResourceLink, stats: Stats):
|
private async getFileMetadata(link: ResourceLink, stats: Stats):
|
||||||
Promise<RepresentationMetadata> {
|
Promise<RepresentationMetadata> {
|
||||||
return (await this.getBaseMetadata(link, stats, false))
|
return (await this.getBaseMetadata(link, stats, false))
|
||||||
.set(CONTENT_TYPE, link.contentType);
|
.set(CONTENT_TYPE_TERM, link.contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,7 +202,7 @@ export class FileDataAccessor implements DataAccessor {
|
|||||||
metadata.remove(RDF.terms.type, LDP.terms.Container);
|
metadata.remove(RDF.terms.type, LDP.terms.Container);
|
||||||
metadata.remove(RDF.terms.type, LDP.terms.BasicContainer);
|
metadata.remove(RDF.terms.type, LDP.terms.BasicContainer);
|
||||||
metadata.removeAll(DC.terms.modified);
|
metadata.removeAll(DC.terms.modified);
|
||||||
metadata.removeAll(CONTENT_TYPE);
|
metadata.removeAll(CONTENT_TYPE_TERM);
|
||||||
const quads = metadata.quads();
|
const quads = metadata.quads();
|
||||||
const metadataLink = await this.resourceMapper.mapUrlToFilePath(link.identifier, true);
|
const metadataLink = await this.resourceMapper.mapUrlToFilePath(link.identifier, true);
|
||||||
let wroteMetadata: boolean;
|
let wroteMetadata: boolean;
|
||||||
|
@ -27,7 +27,7 @@ import { guardStream } from '../../util/GuardedStream';
|
|||||||
import type { Guarded } from '../../util/GuardedStream';
|
import type { Guarded } from '../../util/GuardedStream';
|
||||||
import type { IdentifierStrategy } from '../../util/identifiers/IdentifierStrategy';
|
import type { IdentifierStrategy } from '../../util/identifiers/IdentifierStrategy';
|
||||||
import { isContainerIdentifier } from '../../util/PathUtil';
|
import { isContainerIdentifier } from '../../util/PathUtil';
|
||||||
import { CONTENT_TYPE, LDP } from '../../util/Vocabularies';
|
import { LDP, CONTENT_TYPE_TERM } from '../../util/Vocabularies';
|
||||||
import type { DataAccessor } from './DataAccessor';
|
import type { DataAccessor } from './DataAccessor';
|
||||||
|
|
||||||
const { defaultGraph, namedNode, quad, variable } = DataFactory;
|
const { defaultGraph, namedNode, quad, variable } = DataFactory;
|
||||||
@ -132,7 +132,7 @@ export class SparqlDataAccessor implements DataAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not relevant since all content is triples
|
// Not relevant since all content is triples
|
||||||
metadata.removeAll(CONTENT_TYPE);
|
metadata.removeAll(CONTENT_TYPE_TERM);
|
||||||
|
|
||||||
return this.sendSparqlUpdate(this.sparqlInsert(name, metadata, parent, triples));
|
return this.sendSparqlUpdate(this.sparqlInsert(name, metadata, parent, triples));
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ export class PodQuotaStrategy extends QuotaStrategy {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPimStorageMetadata = metadata!.getAll(RDF.type)
|
const hasPimStorageMetadata = metadata!.getAll(RDF.terms.type)
|
||||||
.some((term): boolean => term.value === PIM.Storage);
|
.some((term): boolean => term.value === PIM.Storage);
|
||||||
|
|
||||||
return hasPimStorageMetadata ? identifier : this.searchPimStorage(parent);
|
return hasPimStorageMetadata ? identifier : this.searchPimStorage(parent);
|
||||||
|
@ -1,35 +1,8 @@
|
|||||||
import { DataFactory } from 'n3';
|
import { DataFactory } from 'n3';
|
||||||
import type { NamedNode, Literal, Term } from 'rdf-js';
|
import type { NamedNode, Literal, Term } from 'rdf-js';
|
||||||
import { CONTENT_TYPE_TERM } from './Vocabularies';
|
|
||||||
|
|
||||||
const { namedNode, literal } = DataFactory;
|
const { namedNode, literal } = DataFactory;
|
||||||
|
|
||||||
// Shorthands for commonly used predicates
|
|
||||||
const shorthands: Record<string, NamedNode> = {
|
|
||||||
contentType: CONTENT_TYPE_TERM,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Caches named node conversions
|
|
||||||
const cachedNamedNodes: Record<string, NamedNode> = {
|
|
||||||
...shorthands,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the incoming name (URI or shorthand) to a named node.
|
|
||||||
* The generated terms get cached to reduce the number of created nodes,
|
|
||||||
* so only use this for internal constants!
|
|
||||||
* @param name - Predicate to potentially transform.
|
|
||||||
*/
|
|
||||||
export function toCachedNamedNode(name: NamedNode | string): NamedNode {
|
|
||||||
if (typeof name !== 'string') {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
if (!(name in cachedNamedNodes)) {
|
|
||||||
cachedNamedNodes[name] = namedNode(name);
|
|
||||||
}
|
|
||||||
return cachedNamedNodes[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param input - Checks if this is a {@link Term}.
|
* @param input - Checks if this is a {@link Term}.
|
||||||
*/
|
*/
|
||||||
|
@ -45,7 +45,7 @@ describe('A LockingResourceStore', (): void => {
|
|||||||
|
|
||||||
// Initialize store
|
// Initialize store
|
||||||
const metadata = new RepresentationMetadata({ path: base }, TEXT_TURTLE);
|
const metadata = new RepresentationMetadata({ path: base }, TEXT_TURTLE);
|
||||||
metadata.add(RDF.type, PIM.terms.Storage);
|
metadata.add(RDF.terms.type, PIM.terms.Storage);
|
||||||
await source.setRepresentation({ path: base }, new BasicRepresentation([], metadata));
|
await source.setRepresentation({ path: base }, new BasicRepresentation([], metadata));
|
||||||
|
|
||||||
locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker());
|
locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker());
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DataFactory } from 'n3';
|
||||||
import type { AuxiliaryIdentifierStrategy } from '../../../../src/http/auxiliary/AuxiliaryIdentifierStrategy';
|
import type { AuxiliaryIdentifierStrategy } from '../../../../src/http/auxiliary/AuxiliaryIdentifierStrategy';
|
||||||
import { LinkMetadataGenerator } from '../../../../src/http/auxiliary/LinkMetadataGenerator';
|
import { LinkMetadataGenerator } from '../../../../src/http/auxiliary/LinkMetadataGenerator';
|
||||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||||
@ -35,7 +36,7 @@ describe('A LinkMetadataGenerator', (): void => {
|
|||||||
const metadata = new RepresentationMetadata(subjectId);
|
const metadata = new RepresentationMetadata(subjectId);
|
||||||
await expect(generator.handle(metadata)).resolves.toBeUndefined();
|
await expect(generator.handle(metadata)).resolves.toBeUndefined();
|
||||||
expect(metadata.quads()).toHaveLength(1);
|
expect(metadata.quads()).toHaveLength(1);
|
||||||
expect(metadata.get(link)?.value).toBe(auxiliaryId.path);
|
expect(metadata.get(DataFactory.namedNode(link))?.value).toBe(auxiliaryId.path);
|
||||||
expect(metadata.getAll(link, SOLID_META.terms.ResponseMetadata)).toHaveLength(1);
|
expect(metadata.getAll(DataFactory.namedNode(link), SOLID_META.terms.ResponseMetadata)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,14 +22,14 @@ describe('A LinkParser', (): void => {
|
|||||||
request.headers.link = '<http://test.com/type>;rel="type"';
|
request.headers.link = '<http://test.com/type>;rel="type"';
|
||||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||||
expect(metadata.quads()).toHaveLength(1);
|
expect(metadata.quads()).toHaveLength(1);
|
||||||
expect(metadata.get(RDF.type)?.value).toBe('http://test.com/type');
|
expect(metadata.get(RDF.terms.type)?.value).toBe('http://test.com/type');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports multiple link headers.', async(): Promise<void> => {
|
it('supports multiple link headers.', async(): Promise<void> => {
|
||||||
request.headers.link = [ '<http://test.com/typeA>;rel="type"', '<http://test.com/typeB>;rel=type' ];
|
request.headers.link = [ '<http://test.com/typeA>;rel="type"', '<http://test.com/typeB>;rel=type' ];
|
||||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||||
expect(metadata.quads()).toHaveLength(2);
|
expect(metadata.quads()).toHaveLength(2);
|
||||||
expect(metadata.getAll(RDF.type).map((term): any => term.value))
|
expect(metadata.getAll(RDF.terms.type).map((term): any => term.value))
|
||||||
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ describe('A LinkParser', (): void => {
|
|||||||
request.headers.link = '<http://test.com/typeA>;rel="type" , <http://test.com/typeB>;rel=type';
|
request.headers.link = '<http://test.com/typeA>;rel="type" , <http://test.com/typeB>;rel=type';
|
||||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||||
expect(metadata.quads()).toHaveLength(2);
|
expect(metadata.quads()).toHaveLength(2);
|
||||||
expect(metadata.getAll(RDF.type).map((term): any => term.value))
|
expect(metadata.getAll(RDF.terms.type).map((term): any => term.value))
|
||||||
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
.toEqual([ 'http://test.com/typeA', 'http://test.com/typeB' ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,6 +30,6 @@ describe('A SlugParser', (): void => {
|
|||||||
request.headers.slug = 'slugA';
|
request.headers.slug = 'slugA';
|
||||||
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
await expect(parser.handle({ request, metadata })).resolves.toBeUndefined();
|
||||||
expect(metadata.quads()).toHaveLength(1);
|
expect(metadata.quads()).toHaveLength(1);
|
||||||
expect(metadata.get(SOLID_HTTP.slug)?.value).toBe('slugA');
|
expect(metadata.get(SOLID_HTTP.terms.slug)?.value).toBe('slugA');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,7 +42,7 @@ describe('A PatchOperationHandler', (): void => {
|
|||||||
expect(store.modifyResource).toHaveBeenCalledTimes(1);
|
expect(store.modifyResource).toHaveBeenCalledTimes(1);
|
||||||
expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
expect(result.statusCode).toBe(201);
|
expect(result.statusCode).toBe(201);
|
||||||
expect(result.metadata?.get(SOLID_HTTP.location)?.value).toBe(operation.target.path);
|
expect(result.metadata?.get(SOLID_HTTP.terms.location)?.value).toBe(operation.target.path);
|
||||||
expect(result.data).toBeUndefined();
|
expect(result.data).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ describe('A PostOperationHandler', (): void => {
|
|||||||
const result = await handler.handle({ operation });
|
const result = await handler.handle({ operation });
|
||||||
expect(result.statusCode).toBe(201);
|
expect(result.statusCode).toBe(201);
|
||||||
expect(result.metadata).toBeInstanceOf(RepresentationMetadata);
|
expect(result.metadata).toBeInstanceOf(RepresentationMetadata);
|
||||||
expect(result.metadata?.get(SOLID_HTTP.location)?.value).toBe('newPath');
|
expect(result.metadata?.get(SOLID_HTTP.terms.location)?.value).toBe('newPath');
|
||||||
expect(result.data).toBeUndefined();
|
expect(result.data).toBeUndefined();
|
||||||
expect(store.addResource).toHaveBeenCalledTimes(1);
|
expect(store.addResource).toHaveBeenCalledTimes(1);
|
||||||
expect(store.addResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
expect(store.addResource).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
|
@ -41,7 +41,7 @@ describe('A PutOperationHandler', (): void => {
|
|||||||
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
expect(store.setRepresentation).toHaveBeenCalledTimes(1);
|
||||||
expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions);
|
||||||
expect(result.statusCode).toBe(201);
|
expect(result.statusCode).toBe(201);
|
||||||
expect(result.metadata?.get(SOLID_HTTP.location)?.value).toBe(operation.target.path);
|
expect(result.metadata?.get(SOLID_HTTP.terms.location)?.value).toBe(operation.target.path);
|
||||||
expect(result.data).toBeUndefined();
|
expect(result.data).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import type { BlankNode } from 'n3';
|
|||||||
import { DataFactory } from 'n3';
|
import { DataFactory } from 'n3';
|
||||||
import type { NamedNode, Quad } from 'rdf-js';
|
import type { NamedNode, Quad } from 'rdf-js';
|
||||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||||
import { CONTENT_TYPE, SOLID_META, RDFS } from '../../../../src/util/Vocabularies';
|
import { CONTENT_TYPE_TERM, SOLID_META, RDFS } from '../../../../src/util/Vocabularies';
|
||||||
const { defaultGraph, literal, namedNode, quad } = DataFactory;
|
const { defaultGraph, literal, namedNode, quad } = DataFactory;
|
||||||
|
|
||||||
// Helper functions to filter quads
|
// Helper functions to filter quads
|
||||||
@ -82,14 +82,14 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
|
|
||||||
it('takes overrides for specific predicates.', async(): Promise<void> => {
|
it('takes overrides for specific predicates.', async(): Promise<void> => {
|
||||||
metadata = new RepresentationMetadata({ predVal: 'objVal' });
|
metadata = new RepresentationMetadata({ predVal: 'objVal' });
|
||||||
expect(metadata.get('predVal')).toEqualRdfTerm(literal('objVal'));
|
expect(metadata.get(namedNode('predVal'))).toEqualRdfTerm(literal('objVal'));
|
||||||
|
|
||||||
metadata = new RepresentationMetadata({ predVal: literal('objVal') });
|
metadata = new RepresentationMetadata({ predVal: literal('objVal') });
|
||||||
expect(metadata.get('predVal')).toEqualRdfTerm(literal('objVal'));
|
expect(metadata.get(namedNode('predVal'))).toEqualRdfTerm(literal('objVal'));
|
||||||
|
|
||||||
metadata = new RepresentationMetadata({ predVal: [ 'objVal1', literal('objVal2') ], predVal2: 'objVal3' });
|
metadata = new RepresentationMetadata({ predVal: [ 'objVal1', literal('objVal2') ], predVal2: 'objVal3' });
|
||||||
expect(metadata.getAll('predVal')).toEqualRdfTermArray([ literal('objVal1'), literal('objVal2') ]);
|
expect(metadata.getAll(namedNode('predVal'))).toEqualRdfTermArray([ literal('objVal1'), literal('objVal2') ]);
|
||||||
expect(metadata.get('predVal2')).toEqualRdfTerm(literal('objVal3'));
|
expect(metadata.get(namedNode('predVal2'))).toEqualRdfTerm(literal('objVal3'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can combine overrides with an identifier.', async(): Promise<void> => {
|
it('can combine overrides with an identifier.', async(): Promise<void> => {
|
||||||
@ -153,7 +153,7 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
// `setMetadata` should have the same result as the following
|
// `setMetadata` should have the same result as the following
|
||||||
const expectedMetadata = new RepresentationMetadata(identifier).addQuads(inputQuads);
|
const expectedMetadata = new RepresentationMetadata(identifier).addQuads(inputQuads);
|
||||||
expectedMetadata.identifier = namedNode('otherId');
|
expectedMetadata.identifier = namedNode('otherId');
|
||||||
expectedMetadata.add('test:pred', 'objVal');
|
expectedMetadata.add(namedNode('test:pred'), 'objVal');
|
||||||
|
|
||||||
expect(metadata.identifier).toEqual(other.identifier);
|
expect(metadata.identifier).toEqual(other.identifier);
|
||||||
expect(metadata.quads()).toBeRdfIsomorphic(expectedMetadata.quads());
|
expect(metadata.quads()).toBeRdfIsomorphic(expectedMetadata.quads());
|
||||||
@ -161,13 +161,13 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
|
|
||||||
it('can add a quad.', async(): Promise<void> => {
|
it('can add a quad.', async(): Promise<void> => {
|
||||||
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'));
|
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'));
|
||||||
metadata.addQuad('random', 'new', 'triple');
|
metadata.addQuad('random', namedNode('new'), 'triple');
|
||||||
expect(metadata.quads()).toBeRdfIsomorphic([ ...inputQuads, newQuad ]);
|
expect(metadata.quads()).toBeRdfIsomorphic([ ...inputQuads, newQuad ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add a quad with a graph.', async(): Promise<void> => {
|
it('can add a quad with a graph.', async(): Promise<void> => {
|
||||||
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'), namedNode('graph'));
|
const newQuad = quad(namedNode('random'), namedNode('new'), literal('triple'), namedNode('graph'));
|
||||||
metadata.addQuad('random', 'new', 'triple', 'graph');
|
metadata.addQuad('random', namedNode('new'), 'triple', 'graph');
|
||||||
expect(metadata.quads()).toBeRdfIsomorphic([ ...inputQuads, newQuad ]);
|
expect(metadata.quads()).toBeRdfIsomorphic([ ...inputQuads, newQuad ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('removes all matching triples if graph is undefined.', async(): Promise<void> => {
|
it('removes all matching triples if graph is undefined.', async(): Promise<void> => {
|
||||||
metadata.removeQuad(identifier, 'has', 'data');
|
metadata.removeQuad(identifier, namedNode('has'), 'data');
|
||||||
expect(metadata.quads()).toHaveLength(inputQuads.length - 2);
|
expect(metadata.quads()).toHaveLength(inputQuads.length - 2);
|
||||||
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has', 'data'));
|
expect(metadata.quads()).toBeRdfIsomorphic(removeQuads(inputQuads, identifier.value, 'has', 'data'));
|
||||||
});
|
});
|
||||||
@ -277,7 +277,6 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
|
|
||||||
it('errors if there are multiple values when getting a value.', async(): Promise<void> => {
|
it('errors if there are multiple values when getting a value.', async(): Promise<void> => {
|
||||||
expect((): any => metadata.get(namedNode('has'))).toThrow(Error);
|
expect((): any => metadata.get(namedNode('has'))).toThrow(Error);
|
||||||
expect((): any => metadata.get('has')).toThrow(Error);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can set the value of a predicate.', async(): Promise<void> => {
|
it('can set the value of a predicate.', async(): Promise<void> => {
|
||||||
@ -293,15 +292,15 @@ describe('A RepresentationMetadata', (): void => {
|
|||||||
it('has a shorthand for content-type.', async(): Promise<void> => {
|
it('has a shorthand for content-type.', async(): Promise<void> => {
|
||||||
expect(metadata.contentType).toBeUndefined();
|
expect(metadata.contentType).toBeUndefined();
|
||||||
metadata.contentType = 'a/b';
|
metadata.contentType = 'a/b';
|
||||||
expect(metadata.get(CONTENT_TYPE)).toEqualRdfTerm(literal('a/b'));
|
expect(metadata.get(CONTENT_TYPE_TERM)).toEqualRdfTerm(literal('a/b'));
|
||||||
expect(metadata.contentType).toBe('a/b');
|
expect(metadata.contentType).toBe('a/b');
|
||||||
metadata.contentType = undefined;
|
metadata.contentType = undefined;
|
||||||
expect(metadata.contentType).toBeUndefined();
|
expect(metadata.contentType).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if a shorthand has multiple values.', async(): Promise<void> => {
|
it('errors if a shorthand has multiple values.', async(): Promise<void> => {
|
||||||
metadata.add(CONTENT_TYPE, 'a/b');
|
metadata.add(CONTENT_TYPE_TERM, 'a/b');
|
||||||
metadata.add(CONTENT_TYPE, 'c/d');
|
metadata.add(CONTENT_TYPE_TERM, 'c/d');
|
||||||
expect((): any => metadata.contentType).toThrow();
|
expect((): any => metadata.contentType).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { DataFactory } from 'n3';
|
||||||
import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
|
import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
|
||||||
import { TemplatedResourcesGenerator } from '../../../../src/pods/generate/TemplatedResourcesGenerator';
|
import { TemplatedResourcesGenerator } from '../../../../src/pods/generate/TemplatedResourcesGenerator';
|
||||||
import type {
|
import type {
|
||||||
@ -10,6 +11,8 @@ import { readableToString } from '../../../../src/util/StreamUtil';
|
|||||||
import { HandlebarsTemplateEngine } from '../../../../src/util/templates/HandlebarsTemplateEngine';
|
import { HandlebarsTemplateEngine } from '../../../../src/util/templates/HandlebarsTemplateEngine';
|
||||||
import { mockFs } from '../../../util/Util';
|
import { mockFs } from '../../../util/Util';
|
||||||
|
|
||||||
|
const { namedNode } = DataFactory;
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
class DummyFactory implements FileIdentifierMapperFactory {
|
class DummyFactory implements FileIdentifierMapperFactory {
|
||||||
@ -115,7 +118,7 @@ describe('A TemplatedResourcesGenerator', (): void => {
|
|||||||
const rootMetadata = result[0].representation.metadata;
|
const rootMetadata = result[0].representation.metadata;
|
||||||
expect(rootMetadata.identifier.value).toBe(location.path);
|
expect(rootMetadata.identifier.value).toBe(location.path);
|
||||||
expect(rootMetadata.quads()).toHaveLength(2);
|
expect(rootMetadata.quads()).toHaveLength(2);
|
||||||
expect(rootMetadata.get('pre:has')?.value).toBe('metadata');
|
expect(rootMetadata.get(namedNode('pre:has'))?.value).toBe('metadata');
|
||||||
expect(rootMetadata.contentType).toBe('text/turtle');
|
expect(rootMetadata.contentType).toBe('text/turtle');
|
||||||
|
|
||||||
// Container has no metadata triples besides content-type
|
// Container has no metadata triples besides content-type
|
||||||
@ -128,7 +131,7 @@ describe('A TemplatedResourcesGenerator', (): void => {
|
|||||||
const docMetadata = result[2].representation.metadata;
|
const docMetadata = result[2].representation.metadata;
|
||||||
expect(docMetadata.identifier.value).toBe(`${location.path}container/template`);
|
expect(docMetadata.identifier.value).toBe(`${location.path}container/template`);
|
||||||
expect(docMetadata.quads()).toHaveLength(2);
|
expect(docMetadata.quads()).toHaveLength(2);
|
||||||
expect(docMetadata.get('pre:has')?.value).toBe('metadata');
|
expect(docMetadata.get(namedNode('pre:has'))?.value).toBe('metadata');
|
||||||
expect(docMetadata.contentType).toBe('text/turtle');
|
expect(docMetadata.contentType).toBe('text/turtle');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -39,7 +39,7 @@ describe('PodQuotaStrategy', (): void => {
|
|||||||
async(identifier: ResourceIdentifier): Promise<RepresentationMetadata> => {
|
async(identifier: ResourceIdentifier): Promise<RepresentationMetadata> => {
|
||||||
const res = new RepresentationMetadata();
|
const res = new RepresentationMetadata();
|
||||||
if (identifier.path === `${base}nested/`) {
|
if (identifier.path === `${base}nested/`) {
|
||||||
res.add(RDF.type, PIM.Storage);
|
res.add(RDF.terms.type, PIM.Storage);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,8 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
expect(result).toMatchObject({ binary: true });
|
expect(result).toMatchObject({ binary: true });
|
||||||
expect(await arrayifyStream(result.data)).toEqual([ resourceData ]);
|
expect(await arrayifyStream(result.data)).toEqual([ resourceData ]);
|
||||||
expect(result.metadata.contentType).toBe('text/plain');
|
expect(result.metadata.contentType).toBe('text/plain');
|
||||||
expect(result.metadata.get('AUXILIARY')?.value).toBe(auxiliaryStrategy.getAuxiliaryIdentifier(resourceID).path);
|
expect(result.metadata.get(namedNode('AUXILIARY'))?.value)
|
||||||
|
.toBe(auxiliaryStrategy.getAuxiliaryIdentifier(resourceID).path);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will return a data stream that matches the metadata for containers.', async(): Promise<void> => {
|
it('will return a data stream that matches the metadata for containers.', async(): Promise<void> => {
|
||||||
@ -196,7 +197,8 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
expect(result).toMatchObject({ binary: false });
|
expect(result).toMatchObject({ binary: false });
|
||||||
expect(await arrayifyStream(result.data)).toBeRdfIsomorphic(metaMirror.quads());
|
expect(await arrayifyStream(result.data)).toBeRdfIsomorphic(metaMirror.quads());
|
||||||
expect(result.metadata.contentType).toEqual(INTERNAL_QUADS);
|
expect(result.metadata.contentType).toEqual(INTERNAL_QUADS);
|
||||||
expect(result.metadata.get('AUXILIARY')?.value).toBe(auxiliaryStrategy.getAuxiliaryIdentifier(resourceID).path);
|
expect(result.metadata.get(namedNode('AUXILIARY'))?.value)
|
||||||
|
.toBe(auxiliaryStrategy.getAuxiliaryIdentifier(resourceID).path);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will remove containment triples referencing auxiliary resources.', async(): Promise<void> => {
|
it('will remove containment triples referencing auxiliary resources.', async(): Promise<void> => {
|
||||||
@ -255,13 +257,13 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('errors when trying to create a container with non-RDF data.', async(): Promise<void> => {
|
it('errors when trying to create a container with non-RDF data.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
await expect(store.addResource(resourceID, representation)).rejects.toThrow(BadRequestHttpError);
|
await expect(store.addResource(resourceID, representation)).rejects.toThrow(BadRequestHttpError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can write resources.', async(): Promise<void> => {
|
it('can write resources.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
path: expect.stringMatching(new RegExp(`^${root}[^/]+$`, 'u')),
|
path: expect.stringMatching(new RegExp(`^${root}[^/]+$`, 'u')),
|
||||||
@ -272,7 +274,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('can write containers.', async(): Promise<void> => {
|
it('can write containers.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
representation.metadata.contentType = 'text/turtle';
|
representation.metadata.contentType = 'text/turtle';
|
||||||
representation.data = guardedStreamFrom([ '<> a <http://test.com/coolContainer>.' ]);
|
representation.data = guardedStreamFrom([ '<> a <http://test.com/coolContainer>.' ]);
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
@ -291,8 +293,8 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('creates a URI based on the incoming slug.', async(): Promise<void> => {
|
it('creates a URI based on the incoming slug.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'newName');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'newName');
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
path: `${root}newName`,
|
path: `${root}newName`,
|
||||||
@ -301,8 +303,8 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('errors on a slug ending on / without Link rel:type Container header.', async(): Promise<void> => {
|
it('errors on a slug ending on / without Link rel:type Container header.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'noContainer/');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'noContainer/');
|
||||||
representation.data = guardedStreamFrom([ `` ]);
|
representation.data = guardedStreamFrom([ `` ]);
|
||||||
const result = store.addResource(resourceID, representation);
|
const result = store.addResource(resourceID, representation);
|
||||||
|
|
||||||
@ -314,9 +316,9 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
it('creates a URI when the incoming slug does not end with /, ' +
|
it('creates a URI when the incoming slug does not end with /, ' +
|
||||||
'but has a Link rel:type Container header.', async(): Promise<void> => {
|
'but has a Link rel:type Container header.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'newContainer');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'newContainer');
|
||||||
representation.data = guardedStreamFrom([ `` ]);
|
representation.data = guardedStreamFrom([ `` ]);
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@ -326,7 +328,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('generates a new URI if adding the slug would create an existing URI.', async(): Promise<void> => {
|
it('generates a new URI if adding the slug would create an existing URI.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'newName');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'newName');
|
||||||
accessor.data[`${root}newName`] = representation;
|
accessor.data[`${root}newName`] = representation;
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
expect(result).not.toEqual({
|
expect(result).not.toEqual({
|
||||||
@ -339,17 +341,17 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('generates http://test.com/%26%26 when slug is &%26.', async(): Promise<void> => {
|
it('generates http://test.com/%26%26 when slug is &%26.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, '&%26');
|
representation.metadata.add(SOLID_HTTP.terms.slug, '&%26');
|
||||||
const result = await store.addResource(resourceID, representation);
|
const result = await store.addResource(resourceID, representation);
|
||||||
expect(result).toEqual({ path: `${root}%26%26` });
|
expect(result).toEqual({ path: `${root}%26%26` });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if the slug contains a slash.', async(): Promise<void> => {
|
it('errors if the slug contains a slash.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.data = guardedStreamFrom([ `` ]);
|
representation.data = guardedStreamFrom([ `` ]);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'sla/sh/es');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'sla/sh/es');
|
||||||
const result = store.addResource(resourceID, representation);
|
const result = store.addResource(resourceID, representation);
|
||||||
await expect(result).rejects.toThrow(BadRequestHttpError);
|
await expect(result).rejects.toThrow(BadRequestHttpError);
|
||||||
await expect(result).rejects.toThrow('Slugs should not contain slashes');
|
await expect(result).rejects.toThrow('Slugs should not contain slashes');
|
||||||
@ -357,8 +359,8 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('errors if the slug would cause an auxiliary resource URI to be generated.', async(): Promise<void> => {
|
it('errors if the slug would cause an auxiliary resource URI to be generated.', async(): Promise<void> => {
|
||||||
const resourceID = { path: root };
|
const resourceID = { path: root };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.add(SOLID_HTTP.slug, 'test.dummy');
|
representation.metadata.add(SOLID_HTTP.terms.slug, 'test.dummy');
|
||||||
const result = store.addResource(resourceID, representation);
|
const result = store.addResource(resourceID, representation);
|
||||||
await expect(result).rejects.toThrow(ForbiddenHttpError);
|
await expect(result).rejects.toThrow(ForbiddenHttpError);
|
||||||
await expect(result).rejects.toThrow('Slug bodies that would result in an auxiliary resource are forbidden');
|
await expect(result).rejects.toThrow('Slug bodies that would result in an auxiliary resource are forbidden');
|
||||||
@ -402,7 +404,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
const mock = jest.spyOn(accessor, 'getMetadata');
|
const mock = jest.spyOn(accessor, 'getMetadata');
|
||||||
|
|
||||||
const resourceID = { path: `${root}` };
|
const resourceID = { path: `${root}` };
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.contentType = 'text/turtle';
|
representation.metadata.contentType = 'text/turtle';
|
||||||
representation.data = guardedStreamFrom([ `<${root}> a <coolContainer>.` ]);
|
representation.data = guardedStreamFrom([ `<${root}> a <coolContainer>.` ]);
|
||||||
|
|
||||||
@ -416,7 +418,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('will error if path does not end in slash and does not match its resource type.', async(): Promise<void> => {
|
it('will error if path does not end in slash and does not match its resource type.', async(): Promise<void> => {
|
||||||
const resourceID = { path: `${root}resource` };
|
const resourceID = { path: `${root}resource` };
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
await expect(store.setRepresentation(resourceID, representation)).rejects.toThrow(
|
await expect(store.setRepresentation(resourceID, representation)).rejects.toThrow(
|
||||||
new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.'),
|
new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.'),
|
||||||
);
|
);
|
||||||
@ -424,7 +426,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('errors when trying to create a container with non-RDF data.', async(): Promise<void> => {
|
it('errors when trying to create a container with non-RDF data.', async(): Promise<void> => {
|
||||||
const resourceID = { path: `${root}container/` };
|
const resourceID = { path: `${root}container/` };
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
await expect(store.setRepresentation(resourceID, representation)).rejects.toThrow(BadRequestHttpError);
|
await expect(store.setRepresentation(resourceID, representation)).rejects.toThrow(BadRequestHttpError);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -450,7 +452,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
const resourceID = { path: `${root}container/` };
|
const resourceID = { path: `${root}container/` };
|
||||||
|
|
||||||
// Generate based on URI
|
// Generate based on URI
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.contentType = 'text/turtle';
|
representation.metadata.contentType = 'text/turtle';
|
||||||
representation.data = guardedStreamFrom([ `<${root}resource/> a <coolContainer>.` ]);
|
representation.data = guardedStreamFrom([ `<${root}resource/> a <coolContainer>.` ]);
|
||||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||||
@ -488,15 +490,15 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('does not write generated metadata.', async(): Promise<void> => {
|
it('does not write generated metadata.', async(): Promise<void> => {
|
||||||
const resourceID = { path: `${root}resource` };
|
const resourceID = { path: `${root}resource` };
|
||||||
representation.metadata.add('notGen', 'value');
|
representation.metadata.add(namedNode('notGen'), 'value');
|
||||||
representation.metadata.add('gen', 'value', SOLID_META.terms.ResponseMetadata);
|
representation.metadata.add(namedNode('gen'), 'value', SOLID_META.terms.ResponseMetadata);
|
||||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||||
{ path: root },
|
{ path: root },
|
||||||
{ path: `${root}resource` },
|
{ path: `${root}resource` },
|
||||||
]);
|
]);
|
||||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||||
expect(accessor.data[resourceID.path].metadata.get('notGen')?.value).toBe('value');
|
expect(accessor.data[resourceID.path].metadata.get(namedNode('notGen'))?.value).toBe('value');
|
||||||
expect(accessor.data[resourceID.path].metadata.get('gen')).toBeUndefined();
|
expect(accessor.data[resourceID.path].metadata.get(namedNode('gen'))).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can write resources even if root does not exist.', async(): Promise<void> => {
|
it('can write resources even if root does not exist.', async(): Promise<void> => {
|
||||||
@ -514,7 +516,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
const resourceID = { path: `${root}container/` };
|
const resourceID = { path: `${root}container/` };
|
||||||
|
|
||||||
// Generate based on URI
|
// Generate based on URI
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.contentType = 'internal/quads';
|
representation.metadata.contentType = 'internal/quads';
|
||||||
representation.data = guardedStreamFrom(
|
representation.data = guardedStreamFrom(
|
||||||
[ quad(namedNode(`${root}resource/`), namedNode('a'), namedNode('coolContainer')) ],
|
[ quad(namedNode(`${root}resource/`), namedNode('a'), namedNode('coolContainer')) ],
|
||||||
@ -529,7 +531,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('errors when trying to create a container with containment triples.', async(): Promise<void> => {
|
it('errors when trying to create a container with containment triples.', async(): Promise<void> => {
|
||||||
const resourceID = { path: `${root}container/` };
|
const resourceID = { path: `${root}container/` };
|
||||||
representation.metadata.add(RDF.type, LDP.terms.Container);
|
representation.metadata.add(RDF.terms.type, LDP.terms.Container);
|
||||||
representation.metadata.contentType = 'text/turtle';
|
representation.metadata.contentType = 'text/turtle';
|
||||||
representation.metadata.identifier = DataFactory.namedNode(`${root}resource/`);
|
representation.metadata.identifier = DataFactory.namedNode(`${root}resource/`);
|
||||||
representation.data = guardedStreamFrom(
|
representation.data = guardedStreamFrom(
|
||||||
@ -548,9 +550,9 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
{ path: `${root}a/b/resource` },
|
{ path: `${root}a/b/resource` },
|
||||||
]);
|
]);
|
||||||
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
await expect(arrayifyStream(accessor.data[resourceID.path].data)).resolves.toEqual([ resourceData ]);
|
||||||
expect(accessor.data[`${root}a/`].metadata.getAll(RDF.type).map((type): string => type.value))
|
expect(accessor.data[`${root}a/`].metadata.getAll(RDF.terms.type).map((type): string => type.value))
|
||||||
.toContain(LDP.Container);
|
.toContain(LDP.Container);
|
||||||
expect(accessor.data[`${root}a/b/`].metadata.getAll(RDF.type).map((type): string => type.value))
|
expect(accessor.data[`${root}a/b/`].metadata.getAll(RDF.terms.type).map((type): string => type.value))
|
||||||
.toContain(LDP.Container);
|
.toContain(LDP.Container);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -568,7 +570,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
const resourceID = { path: `${root}` };
|
const resourceID = { path: `${root}` };
|
||||||
|
|
||||||
// Generate based on URI
|
// Generate based on URI
|
||||||
representation.metadata.removeAll(RDF.type);
|
representation.metadata.removeAll(RDF.terms.type);
|
||||||
representation.metadata.contentType = 'text/turtle';
|
representation.metadata.contentType = 'text/turtle';
|
||||||
representation.data = guardedStreamFrom([]);
|
representation.data = guardedStreamFrom([]);
|
||||||
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([
|
||||||
@ -620,7 +622,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('will error when deleting a root storage container.', async(): Promise<void> => {
|
it('will error when deleting a root storage container.', async(): Promise<void> => {
|
||||||
representation.metadata.add(RDF.type, PIM.terms.Storage);
|
representation.metadata.add(RDF.terms.type, PIM.terms.Storage);
|
||||||
accessor.data[`${root}container/`] = representation;
|
accessor.data[`${root}container/`] = representation;
|
||||||
const result = store.deleteResource({ path: `${root}container/` });
|
const result = store.deleteResource({ path: `${root}container/` });
|
||||||
await expect(result).rejects.toThrow(MethodNotAllowedHttpError);
|
await expect(result).rejects.toThrow(MethodNotAllowedHttpError);
|
||||||
@ -629,7 +631,7 @@ describe('A DataAccessorBasedStore', (): void => {
|
|||||||
|
|
||||||
it('will error when deleting an auxiliary of a root storage container if not allowed.', async(): Promise<void> => {
|
it('will error when deleting an auxiliary of a root storage container if not allowed.', async(): Promise<void> => {
|
||||||
const storageMetadata = new RepresentationMetadata(representation.metadata);
|
const storageMetadata = new RepresentationMetadata(representation.metadata);
|
||||||
storageMetadata.add(RDF.type, PIM.terms.Storage);
|
storageMetadata.add(RDF.terms.type, PIM.terms.Storage);
|
||||||
accessor.data[`${root}container/`] = new BasicRepresentation(representation.data, storageMetadata);
|
accessor.data[`${root}container/`] = new BasicRepresentation(representation.data, storageMetadata);
|
||||||
accessor.data[`${root}container/.dummy`] = representation;
|
accessor.data[`${root}container/.dummy`] = representation;
|
||||||
auxiliaryStrategy.isRequiredInRoot = jest.fn().mockReturnValue(true);
|
auxiliaryStrategy.isRequiredInRoot = jest.fn().mockReturnValue(true);
|
||||||
|
@ -18,6 +18,8 @@ import { toLiteral } from '../../../../src/util/TermUtil';
|
|||||||
import { CONTENT_TYPE, DC, LDP, POSIX, RDF, SOLID_META, XSD } from '../../../../src/util/Vocabularies';
|
import { CONTENT_TYPE, DC, LDP, POSIX, RDF, SOLID_META, XSD } from '../../../../src/util/Vocabularies';
|
||||||
import { mockFs } from '../../../util/Util';
|
import { mockFs } from '../../../util/Util';
|
||||||
|
|
||||||
|
const { namedNode } = DataFactory;
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
const rootFilePath = 'uploads';
|
const rootFilePath = 'uploads';
|
||||||
@ -104,10 +106,11 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
metadata = await accessor.getMetadata({ path: `${base}resource.ttl` });
|
metadata = await accessor.getMetadata({ path: `${base}resource.ttl` });
|
||||||
expect(metadata.identifier.value).toBe(`${base}resource.ttl`);
|
expect(metadata.identifier.value).toBe(`${base}resource.ttl`);
|
||||||
expect(metadata.contentType).toBe('text/turtle');
|
expect(metadata.contentType).toBe('text/turtle');
|
||||||
expect(metadata.get(RDF.type)?.value).toBe(LDP.Resource);
|
expect(metadata.get(RDF.terms.type)?.value).toBe(LDP.Resource);
|
||||||
expect(metadata.get(POSIX.size)).toEqualRdfTerm(toLiteral('data'.length, XSD.terms.integer));
|
expect(metadata.get(POSIX.terms.size)).toEqualRdfTerm(toLiteral('data'.length, XSD.terms.integer));
|
||||||
expect(metadata.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
||||||
expect(metadata.get(POSIX.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), XSD.terms.integer));
|
expect(metadata.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000),
|
||||||
|
XSD.terms.integer));
|
||||||
// `dc:modified` is in the default graph
|
// `dc:modified` is in the default graph
|
||||||
expect(metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata)).toHaveLength(2);
|
expect(metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata)).toHaveLength(2);
|
||||||
});
|
});
|
||||||
@ -115,8 +118,8 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
it('does not generate size metadata for a container.', async(): Promise<void> => {
|
it('does not generate size metadata for a container.', async(): Promise<void> => {
|
||||||
cache.data = { container: {}};
|
cache.data = { container: {}};
|
||||||
metadata = await accessor.getMetadata({ path: `${base}container/` });
|
metadata = await accessor.getMetadata({ path: `${base}container/` });
|
||||||
expect(metadata.get(POSIX.size)).toBeUndefined();
|
expect(metadata.get(POSIX.terms.size)).toBeUndefined();
|
||||||
expect(metadata.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates the metadata for a container.', async(): Promise<void> => {
|
it('generates the metadata for a container.', async(): Promise<void> => {
|
||||||
@ -130,12 +133,13 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
};
|
};
|
||||||
metadata = await accessor.getMetadata({ path: `${base}container/` });
|
metadata = await accessor.getMetadata({ path: `${base}container/` });
|
||||||
expect(metadata.identifier.value).toBe(`${base}container/`);
|
expect(metadata.identifier.value).toBe(`${base}container/`);
|
||||||
expect(metadata.getAll(RDF.type)).toEqualRdfTermArray(
|
expect(metadata.getAll(RDF.terms.type)).toEqualRdfTermArray(
|
||||||
[ LDP.terms.Container, LDP.terms.BasicContainer, LDP.terms.Resource ],
|
[ LDP.terms.Container, LDP.terms.BasicContainer, LDP.terms.Resource ],
|
||||||
);
|
);
|
||||||
expect(metadata.get(POSIX.size)).toBeUndefined();
|
expect(metadata.get(POSIX.terms.size)).toBeUndefined();
|
||||||
expect(metadata.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
||||||
expect(metadata.get(POSIX.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), XSD.terms.integer));
|
expect(metadata.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000),
|
||||||
|
XSD.terms.integer));
|
||||||
// `dc:modified` is in the default graph
|
// `dc:modified` is in the default graph
|
||||||
expect(metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata)).toHaveLength(1);
|
expect(metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
@ -169,7 +173,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
|
|
||||||
// Containers
|
// Containers
|
||||||
for (const child of children.filter(({ identifier }): boolean => identifier.value.endsWith('/'))) {
|
for (const child of children.filter(({ identifier }): boolean => identifier.value.endsWith('/'))) {
|
||||||
const types = child.getAll(RDF.type).map((term): string => term.value);
|
const types = child.getAll(RDF.terms.type).map((term): string => term.value);
|
||||||
expect(types).toContain(LDP.Resource);
|
expect(types).toContain(LDP.Resource);
|
||||||
expect(types).toContain(LDP.Container);
|
expect(types).toContain(LDP.Container);
|
||||||
expect(types).toContain(LDP.BasicContainer);
|
expect(types).toContain(LDP.BasicContainer);
|
||||||
@ -177,7 +181,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
|
|
||||||
// Documents
|
// Documents
|
||||||
for (const child of children.filter(({ identifier }): boolean => !identifier.value.endsWith('/'))) {
|
for (const child of children.filter(({ identifier }): boolean => !identifier.value.endsWith('/'))) {
|
||||||
const types = child.getAll(RDF.type).map((term): string => term.value);
|
const types = child.getAll(RDF.terms.type).map((term): string => term.value);
|
||||||
expect(types).toContain(LDP.Resource);
|
expect(types).toContain(LDP.Resource);
|
||||||
expect(types).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource');
|
expect(types).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource');
|
||||||
expect(types).not.toContain(LDP.Container);
|
expect(types).not.toContain(LDP.Container);
|
||||||
@ -186,8 +190,8 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
|
|
||||||
// All resources
|
// All resources
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
expect(child.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
expect(child.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime));
|
||||||
expect(child.get(POSIX.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000),
|
expect(child.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000),
|
||||||
XSD.terms.integer));
|
XSD.terms.integer));
|
||||||
// `dc:modified` is in the default graph
|
// `dc:modified` is in the default graph
|
||||||
expect(child.quads(null, null, null, SOLID_META.terms.ResponseMetadata))
|
expect(child.quads(null, null, null, SOLID_META.terms.ResponseMetadata))
|
||||||
@ -228,8 +232,8 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
`${base}container/resource2`,
|
`${base}container/resource2`,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const types1 = children[0].getAll(RDF.type).map((term): string => term.value);
|
const types1 = children[0].getAll(RDF.terms.type).map((term): string => term.value);
|
||||||
const types2 = children[1].getAll(RDF.type).map((term): string => term.value);
|
const types2 = children[1].getAll(RDF.terms.type).map((term): string => term.value);
|
||||||
|
|
||||||
expect(types1).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource');
|
expect(types1).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource');
|
||||||
for (const type of types2) {
|
for (const type of types2) {
|
||||||
@ -279,7 +283,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not write metadata that is stored by the file system.', async(): Promise<void> => {
|
it('does not write metadata that is stored by the file system.', async(): Promise<void> => {
|
||||||
metadata.add(RDF.type, LDP.terms.Resource);
|
metadata.add(RDF.terms.type, LDP.terms.Resource);
|
||||||
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata)).resolves.toBeUndefined();
|
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata)).resolves.toBeUndefined();
|
||||||
expect(cache.data.resource).toBe('data');
|
expect(cache.data.resource).toBe('data');
|
||||||
expect(cache.data['resource.meta']).toBeUndefined();
|
expect(cache.data['resource.meta']).toBeUndefined();
|
||||||
@ -315,7 +319,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
data.emit('error', new Error('error'));
|
data.emit('error', new Error('error'));
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
metadata.add('likes', 'apples');
|
metadata.add(namedNode('likes'), 'apples');
|
||||||
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata))
|
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata))
|
||||||
.rejects.toThrow('error');
|
.rejects.toThrow('error');
|
||||||
expect(cache.data['resource.meta']).toBeUndefined();
|
expect(cache.data['resource.meta']).toBeUndefined();
|
||||||
@ -325,7 +329,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
cache.data = { 'resource$.ttl': '<this> <is> <data>.', 'resource.meta': '<this> <is> <metadata>.' };
|
cache.data = { 'resource$.ttl': '<this> <is> <data>.', 'resource.meta': '<this> <is> <metadata>.' };
|
||||||
metadata.identifier = DataFactory.namedNode(`${base}resource`);
|
metadata.identifier = DataFactory.namedNode(`${base}resource`);
|
||||||
metadata.contentType = 'text/plain';
|
metadata.contentType = 'text/plain';
|
||||||
metadata.add('new', 'metadata');
|
metadata.add(namedNode('new'), 'metadata');
|
||||||
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata))
|
await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata))
|
||||||
.resolves.toBeUndefined();
|
.resolves.toBeUndefined();
|
||||||
expect(cache.data).toEqual({
|
expect(cache.data).toEqual({
|
||||||
@ -337,7 +341,7 @@ describe('A FileDataAccessor', (): void => {
|
|||||||
it('does not try to update the content-type if there is no original file.', async(): Promise<void> => {
|
it('does not try to update the content-type if there is no original file.', async(): Promise<void> => {
|
||||||
metadata.identifier = DataFactory.namedNode(`${base}resource.txt`);
|
metadata.identifier = DataFactory.namedNode(`${base}resource.txt`);
|
||||||
metadata.contentType = 'text/turtle';
|
metadata.contentType = 'text/turtle';
|
||||||
metadata.add('new', 'metadata');
|
metadata.add(namedNode('new'), 'metadata');
|
||||||
await expect(accessor.writeDocument({ path: `${base}resource.txt` }, data, metadata))
|
await expect(accessor.writeDocument({ path: `${base}resource.txt` }, data, metadata))
|
||||||
.resolves.toBeUndefined();
|
.resolves.toBeUndefined();
|
||||||
expect(cache.data).toEqual({
|
expect(cache.data).toEqual({
|
||||||
|
@ -133,14 +133,14 @@ describe('An InMemoryDataAccessor', (): void => {
|
|||||||
)).resolves.toBeUndefined();
|
)).resolves.toBeUndefined();
|
||||||
|
|
||||||
const newMetadata = new RepresentationMetadata(inputMetadata);
|
const newMetadata = new RepresentationMetadata(inputMetadata);
|
||||||
newMetadata.add(RDF.type, LDP.terms.BasicContainer);
|
newMetadata.add(RDF.terms.type, LDP.terms.BasicContainer);
|
||||||
await expect(accessor.writeContainer(identifier, newMetadata)).resolves.toBeUndefined();
|
await expect(accessor.writeContainer(identifier, newMetadata)).resolves.toBeUndefined();
|
||||||
|
|
||||||
metadata = await accessor.getMetadata(identifier);
|
metadata = await accessor.getMetadata(identifier);
|
||||||
expect(metadata.identifier.value).toBe(`${base}container/`);
|
expect(metadata.identifier.value).toBe(`${base}container/`);
|
||||||
const quads = metadata.quads();
|
const quads = metadata.quads();
|
||||||
expect(quads).toHaveLength(2);
|
expect(quads).toHaveLength(2);
|
||||||
expect(metadata.getAll(RDF.type).map((term): string => term.value))
|
expect(metadata.getAll(RDF.terms.type).map((term): string => term.value))
|
||||||
.toEqual([ LDP.Container, LDP.BasicContainer ]);
|
.toEqual([ LDP.Container, LDP.BasicContainer ]);
|
||||||
|
|
||||||
const children = [];
|
const children = [];
|
||||||
@ -168,7 +168,7 @@ describe('An InMemoryDataAccessor', (): void => {
|
|||||||
expect(metadata.identifier.value).toBe(`${base}`);
|
expect(metadata.identifier.value).toBe(`${base}`);
|
||||||
const quads = metadata.quads();
|
const quads = metadata.quads();
|
||||||
expect(quads).toHaveLength(1);
|
expect(quads).toHaveLength(1);
|
||||||
expect(metadata.getAll(RDF.type)).toHaveLength(1);
|
expect(metadata.getAll(RDF.terms.type)).toHaveLength(1);
|
||||||
|
|
||||||
const children = [];
|
const children = [];
|
||||||
for await (const child of accessor.getChildren(identifier)) {
|
for await (const child of accessor.getChildren(identifier)) {
|
||||||
|
@ -63,7 +63,7 @@ describe('A ConstantConverter', (): void => {
|
|||||||
|
|
||||||
it('does not support representations that are already in the right format.', async(): Promise<void> => {
|
it('does not support representations that are already in the right format.', async(): Promise<void> => {
|
||||||
const preferences = { type: { 'text/html': 1 }};
|
const preferences = { type: { 'text/html': 1 }};
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'text/html' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/html' });
|
||||||
const representation = { metadata } as any;
|
const representation = { metadata } as any;
|
||||||
const args = { identifier, representation, preferences };
|
const args = { identifier, representation, preferences };
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ describe('A ConstantConverter', (): void => {
|
|||||||
|
|
||||||
it('replaces the representation of a supported request.', async(): Promise<void> => {
|
it('replaces the representation of a supported request.', async(): Promise<void> => {
|
||||||
const preferences = { type: { 'text/html': 1 }};
|
const preferences = { type: { 'text/html': 1 }};
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'text/turtle' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/turtle' });
|
||||||
const representation = { metadata, data: { destroy: jest.fn() }} as any;
|
const representation = { metadata, data: { destroy: jest.fn() }} as any;
|
||||||
const args = { identifier, representation, preferences };
|
const args = { identifier, representation, preferences };
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import 'jest-rdf';
|
|||||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||||
import { ContentTypeReplacer } from '../../../../src/storage/conversion/ContentTypeReplacer';
|
import { ContentTypeReplacer } from '../../../../src/storage/conversion/ContentTypeReplacer';
|
||||||
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
||||||
|
import { CONTENT_TYPE } from '../../../../src/util/Vocabularies';
|
||||||
|
|
||||||
const binary = true;
|
const binary = true;
|
||||||
const data = { data: true };
|
const data = { data: true };
|
||||||
@ -21,7 +22,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws on an unsupported input type.', async(): Promise<void> => {
|
it('throws on an unsupported input type.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'text/plain' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' });
|
||||||
const representation = { metadata };
|
const representation = { metadata };
|
||||||
const preferences = { type: { 'application/json': 1 }};
|
const preferences = { type: { 'application/json': 1 }};
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws on an unsupported output type.', async(): Promise<void> => {
|
it('throws on an unsupported output type.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' });
|
||||||
const representation = { metadata };
|
const representation = { metadata };
|
||||||
const preferences = { type: { 'application/json': 1 }};
|
const preferences = { type: { 'application/json': 1 }};
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('replaces a supported content type when no preferences are given.', async(): Promise<void> => {
|
it('replaces a supported content type when no preferences are given.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' });
|
||||||
const representation = { binary, data, metadata };
|
const representation = { binary, data, metadata };
|
||||||
const preferences = {};
|
const preferences = {};
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('replaces a supported content type when preferences are given.', async(): Promise<void> => {
|
it('replaces a supported content type when preferences are given.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' });
|
||||||
const representation = { binary, data, metadata };
|
const representation = { binary, data, metadata };
|
||||||
const preferences = { type: { 'application/n-quads': 1 }};
|
const preferences = { type: { 'application/n-quads': 1 }};
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('replaces a supported wildcard type.', async(): Promise<void> => {
|
it('replaces a supported wildcard type.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'text/plain' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' });
|
||||||
const representation = { binary, data, metadata };
|
const representation = { binary, data, metadata };
|
||||||
const preferences = { type: { 'application/octet-stream': 1 }};
|
const preferences = { type: { 'application/octet-stream': 1 }};
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ describe('A ContentTypeReplacer', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('picks the most preferred content type.', async(): Promise<void> => {
|
it('picks the most preferred content type.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' });
|
const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' });
|
||||||
const representation = { binary, data, metadata };
|
const representation = { binary, data, metadata };
|
||||||
const preferences = { type: {
|
const preferences = { type: {
|
||||||
'text/turtle': 0.5,
|
'text/turtle': 0.5,
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import 'jest-rdf';
|
import 'jest-rdf';
|
||||||
import { DataFactory } from 'n3';
|
import { DataFactory } from 'n3';
|
||||||
import {
|
import {
|
||||||
toCachedNamedNode,
|
|
||||||
toNamedTerm,
|
toNamedTerm,
|
||||||
toPredicateTerm,
|
toPredicateTerm,
|
||||||
toObjectTerm,
|
toObjectTerm,
|
||||||
toLiteral,
|
toLiteral,
|
||||||
isTerm,
|
isTerm,
|
||||||
} from '../../../src/util/TermUtil';
|
} from '../../../src/util/TermUtil';
|
||||||
import { CONTENT_TYPE_TERM, XSD } from '../../../src/util/Vocabularies';
|
import { XSD } from '../../../src/util/Vocabularies';
|
||||||
const { literal, namedNode } = DataFactory;
|
const { literal, namedNode } = DataFactory;
|
||||||
|
|
||||||
describe('TermUtil', (): void => {
|
describe('TermUtil', (): void => {
|
||||||
@ -22,27 +21,6 @@ describe('TermUtil', (): void => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toCachedNamedNode function', (): void => {
|
|
||||||
it('returns the input if it was a named node.', async(): Promise<void> => {
|
|
||||||
const term = namedNode('name');
|
|
||||||
expect(toCachedNamedNode(term)).toBe(term);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns a named node when a string is used.', async(): Promise<void> => {
|
|
||||||
expect(toCachedNamedNode('name')).toEqualRdfTerm(namedNode('name'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('caches generated named nodes.', async(): Promise<void> => {
|
|
||||||
const result = toCachedNamedNode('name');
|
|
||||||
expect(result).toEqualRdfTerm(namedNode('name'));
|
|
||||||
expect(toCachedNamedNode('name')).toBe(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports URI shorthands.', async(): Promise<void> => {
|
|
||||||
expect(toCachedNamedNode('contentType')).toEqualRdfTerm(CONTENT_TYPE_TERM);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('toSubjectTerm function', (): void => {
|
describe('toSubjectTerm function', (): void => {
|
||||||
it('returns the input if it was a term.', async(): Promise<void> => {
|
it('returns the input if it was a term.', async(): Promise<void> => {
|
||||||
const nn = namedNode('name');
|
const nn = namedNode('name');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user