diff --git a/src/http/auxiliary/LinkMetadataGenerator.ts b/src/http/auxiliary/LinkMetadataGenerator.ts index da793699a..039297db2 100644 --- a/src/http/auxiliary/LinkMetadataGenerator.ts +++ b/src/http/auxiliary/LinkMetadataGenerator.ts @@ -1,4 +1,5 @@ import { DataFactory } from 'n3'; +import type { NamedNode } from 'rdf-js'; import { SOLID_META } from '../../util/Vocabularies'; import type { RepresentationMetadata } from '../representation/RepresentationMetadata'; 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 */ export class LinkMetadataGenerator extends MetadataGenerator { - private readonly link: string; + private readonly link: NamedNode; private readonly identifierStrategy: AuxiliaryIdentifierStrategy; public constructor(link: string, identifierStrategy: AuxiliaryIdentifierStrategy) { super(); - this.link = link; + this.link = DataFactory.namedNode(link); this.identifierStrategy = identifierStrategy; } diff --git a/src/http/input/metadata/SlugParser.ts b/src/http/input/metadata/SlugParser.ts index 16e30422d..5de6a35a5 100644 --- a/src/http/input/metadata/SlugParser.ts +++ b/src/http/input/metadata/SlugParser.ts @@ -19,7 +19,7 @@ export class SlugParser extends MetadataParser { throw new BadRequestHttpError('Request has multiple Slug headers'); } this.logger.debug(`Request Slug is '${slug}'.`); - input.metadata.set(SOLID_HTTP.slug, slug); + input.metadata.set(SOLID_HTTP.terms.slug, slug); } } } diff --git a/src/http/output/metadata/LinkRelMetadataWriter.ts b/src/http/output/metadata/LinkRelMetadataWriter.ts index fc0ec7658..bb498d523 100644 --- a/src/http/output/metadata/LinkRelMetadataWriter.ts +++ b/src/http/output/metadata/LinkRelMetadataWriter.ts @@ -1,3 +1,5 @@ +import type { NamedNode } from 'n3'; +import { DataFactory } from 'n3'; import { getLoggerFor } from '../../../logging/LogUtil'; import type { HttpResponse } from '../../../server/HttpResponse'; 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. */ export class LinkRelMetadataWriter extends MetadataWriter { - private readonly linkRelMap: Record; + private readonly linkRelMap: Map; protected readonly logger = getLoggerFor(this); public constructor(linkRelMap: Record) { super(); - this.linkRelMap = linkRelMap; + + this.linkRelMap = new Map(); + for (const [ key, value ] of Object.entries(linkRelMap)) { + this.linkRelMap.set(DataFactory.namedNode(key), value); + } } public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise { - const keys = Object.keys(this.linkRelMap); - this.logger.debug(`Available link relations: ${keys.length}`); - for (const key of keys) { - const values = input.metadata.getAll(key).map((term): string => `<${term.value}>; rel="${this.linkRelMap[key]}"`); + this.logger.debug(`Available link relations: ${this.linkRelMap.size}`); + for (const [ predicate, relValue ] of this.linkRelMap) { + const values = input.metadata.getAll(predicate) + .map((term): string => `<${term.value}>; rel="${relValue}"`); if (values.length > 0) { this.logger.debug(`Adding Link header ${values}`); addHeader(input.response, 'Link', values); diff --git a/src/http/output/metadata/MappedMetadataWriter.ts b/src/http/output/metadata/MappedMetadataWriter.ts index c76e82ed6..904d4c432 100644 --- a/src/http/output/metadata/MappedMetadataWriter.ts +++ b/src/http/output/metadata/MappedMetadataWriter.ts @@ -1,3 +1,5 @@ +import type { NamedNode } from 'n3'; +import { DataFactory } from 'n3'; import type { HttpResponse } from '../../../server/HttpResponse'; import { addHeader } from '../../../util/HeaderUtil'; 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). */ export class MappedMetadataWriter extends MetadataWriter { - private readonly headerMap: [string, string][]; + private readonly headerMap: Map; public constructor(headerMap: Record) { super(); - this.headerMap = Object.entries(headerMap); + + this.headerMap = new Map(); + for (const [ key, value ] of Object.entries(headerMap)) { + this.headerMap.set(DataFactory.namedNode(key), value); + } } public async handle(input: { response: HttpResponse; metadata: RepresentationMetadata }): Promise { diff --git a/src/http/representation/RepresentationMetadata.ts b/src/http/representation/RepresentationMetadata.ts index dfd9cc215..2366b3ea2 100644 --- a/src/http/representation/RepresentationMetadata.ts +++ b/src/http/representation/RepresentationMetadata.ts @@ -4,7 +4,7 @@ import { getLoggerFor } from '../../logging/LogUtil'; import { InternalServerError } from '../../util/errors/InternalServerError'; import type { ContentType } 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 type { ResourceIdentifier } from './ResourceIdentifier'; import { isResourceIdentifier } from './ResourceIdentifier'; @@ -21,6 +21,22 @@ export function isRepresentationMetadata(object: any): object is RepresentationM return typeof object?.setMetadata === 'function'; } +// Caches named node conversions +const cachedNamedNodes: Record = {}; + +/** + * 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. * Most functions return the metadata object to allow for chaining. @@ -116,7 +132,7 @@ export class RepresentationMetadata { */ public quads( subject: NamedNode | BlankNode | string | null = null, - predicate: NamedNode | string | null = null, + predicate: NamedNode | null = null, object: NamedNode | BlankNode | Literal | string | null = null, graph: MetadataGraph | null = null, ): Quad[] { @@ -167,12 +183,12 @@ export class RepresentationMetadata { */ public addQuad( subject: NamedNode | BlankNode | string, - predicate: NamedNode | string, + predicate: NamedNode, object: NamedNode | BlankNode | Literal | string, graph?: MetadataGraph, ): this { this.store.addQuad(toNamedTerm(subject), - toCachedNamedNode(predicate), + predicate, toObjectTerm(object, true), graph ? toNamedTerm(graph) : undefined); return this; @@ -194,12 +210,12 @@ export class RepresentationMetadata { */ public removeQuad( subject: NamedNode | BlankNode | string, - predicate: NamedNode | string, + predicate: NamedNode, object: NamedNode | BlankNode | Literal | string, graph?: MetadataGraph, ): this { const quads = this.quads(toNamedTerm(subject), - toCachedNamedNode(predicate), + predicate, toObjectTerm(object, true), graph ? toNamedTerm(graph) : undefined); return this.removeQuads(quads); @@ -219,7 +235,7 @@ export class RepresentationMetadata { * @param object - Value(s) to add. * @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)); } @@ -229,7 +245,7 @@ export class RepresentationMetadata { * @param object - Value(s) to remove. * @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)); } @@ -237,12 +253,11 @@ export class RepresentationMetadata { * 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. */ - private forQuads(predicate: NamedNode | string, object: MetadataValue, + private forQuads(predicate: NamedNode, object: MetadataValue, forFn: (pred: NamedNode, obj: NamedNode | Literal) => void): this { - const predicateNode = toCachedNamedNode(predicate); const objects = Array.isArray(object) ? object : [ object ]; for (const obj of objects) { - forFn(predicateNode, toObjectTerm(obj, true)); + forFn(predicate, toObjectTerm(obj, true)); } return this; } @@ -252,8 +267,8 @@ export class RepresentationMetadata { * @param predicate - Predicate to remove. * @param graph - Optional graph where to remove from. */ - public removeAll(predicate: NamedNode | string, graph?: MetadataGraph): this { - this.removeQuads(this.store.getQuads(this.id, toCachedNamedNode(predicate), null, graph ?? null)); + public removeAll(predicate: NamedNode, graph?: MetadataGraph): this { + this.removeQuads(this.store.getQuads(this.id, predicate, null, graph ?? null)); return this; } @@ -278,8 +293,8 @@ export class RepresentationMetadata { * * @returns An array with all matches. */ - public getAll(predicate: NamedNode | string, graph?: MetadataGraph): Term[] { - return this.store.getQuads(this.id, toCachedNamedNode(predicate), null, graph ?? null) + public getAll(predicate: NamedNode, graph?: MetadataGraph): Term[] { + return this.store.getQuads(this.id, predicate, null, graph ?? null) .map((quad): Term => quad.object); } @@ -292,15 +307,15 @@ export class RepresentationMetadata { * * @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); if (terms.length === 0) { return; } 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( - `Multiple results for ${typeof predicate === 'string' ? predicate : predicate.value}`, + `Multiple results for ${predicate.value}`, ); } return terms[0]; @@ -313,7 +328,7 @@ export class RepresentationMetadata { * @param object - Value(s) to set. * @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); if (object) { this.add(predicate, object, graph); diff --git a/src/storage/DataAccessorBasedStore.ts b/src/storage/DataAccessorBasedStore.ts index ca3f5511c..1ae29cc2d 100644 --- a/src/storage/DataAccessorBasedStore.ts +++ b/src/storage/DataAccessorBasedStore.ts @@ -29,7 +29,6 @@ import { import { parseQuads } from '../util/QuadUtil'; import { addResourceMetadata, updateModifiedDate } from '../util/ResourceUtil'; import { - CONTENT_TYPE, DC, SOLID_HTTP, LDP, @@ -39,6 +38,7 @@ import { XSD, SOLID_META, PREFERRED_PREFIX_TERM, + CONTENT_TYPE_TERM, } from '../util/Vocabularies'; import type { DataAccessor } from './accessors/DataAccessor'; import type { Conditions } from './Conditions'; @@ -435,7 +435,7 @@ export class DataAccessorBasedStore implements ResourceStore { } // 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 representation.metadata.addQuads(quads); @@ -516,8 +516,8 @@ export class DataAccessorBasedStore implements ResourceStore { Promise { // Get all values needed for naming the resource const isContainer = this.isContainerType(metadata); - const slug = metadata.get(SOLID_HTTP.slug)?.value; - metadata.removeAll(SOLID_HTTP.slug); + const slug = metadata.get(SOLID_HTTP.terms.slug)?.value; + metadata.removeAll(SOLID_HTTP.terms.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. */ 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. */ 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); } /** diff --git a/src/storage/accessors/FileDataAccessor.ts b/src/storage/accessors/FileDataAccessor.ts index a07e4c37a..7453fdd8e 100644 --- a/src/storage/accessors/FileDataAccessor.ts +++ b/src/storage/accessors/FileDataAccessor.ts @@ -16,7 +16,7 @@ import { joinFilePath, isContainerIdentifier } from '../../util/PathUtil'; import { parseQuads, serializeQuads } from '../../util/QuadUtil'; import { addResourceMetadata, updateModifiedDate } from '../../util/ResourceUtil'; 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 { DataAccessor } from './DataAccessor'; @@ -174,7 +174,7 @@ export class FileDataAccessor implements DataAccessor { private async getFileMetadata(link: ResourceLink, stats: Stats): Promise { 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.BasicContainer); metadata.removeAll(DC.terms.modified); - metadata.removeAll(CONTENT_TYPE); + metadata.removeAll(CONTENT_TYPE_TERM); const quads = metadata.quads(); const metadataLink = await this.resourceMapper.mapUrlToFilePath(link.identifier, true); let wroteMetadata: boolean; diff --git a/src/storage/accessors/SparqlDataAccessor.ts b/src/storage/accessors/SparqlDataAccessor.ts index 578de3dc3..70662ea91 100644 --- a/src/storage/accessors/SparqlDataAccessor.ts +++ b/src/storage/accessors/SparqlDataAccessor.ts @@ -27,7 +27,7 @@ import { guardStream } from '../../util/GuardedStream'; import type { Guarded } from '../../util/GuardedStream'; import type { IdentifierStrategy } from '../../util/identifiers/IdentifierStrategy'; 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'; const { defaultGraph, namedNode, quad, variable } = DataFactory; @@ -132,7 +132,7 @@ export class SparqlDataAccessor implements DataAccessor { } // 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)); } diff --git a/src/storage/quota/PodQuotaStrategy.ts b/src/storage/quota/PodQuotaStrategy.ts index 803d59501..1f3758ee3 100644 --- a/src/storage/quota/PodQuotaStrategy.ts +++ b/src/storage/quota/PodQuotaStrategy.ts @@ -57,7 +57,7 @@ export class PodQuotaStrategy extends QuotaStrategy { throw error; } - const hasPimStorageMetadata = metadata!.getAll(RDF.type) + const hasPimStorageMetadata = metadata!.getAll(RDF.terms.type) .some((term): boolean => term.value === PIM.Storage); return hasPimStorageMetadata ? identifier : this.searchPimStorage(parent); diff --git a/src/util/TermUtil.ts b/src/util/TermUtil.ts index 3a55b789d..89dc01736 100644 --- a/src/util/TermUtil.ts +++ b/src/util/TermUtil.ts @@ -1,35 +1,8 @@ import { DataFactory } from 'n3'; import type { NamedNode, Literal, Term } from 'rdf-js'; -import { CONTENT_TYPE_TERM } from './Vocabularies'; const { namedNode, literal } = DataFactory; -// Shorthands for commonly used predicates -const shorthands: Record = { - contentType: CONTENT_TYPE_TERM, -}; - -// Caches named node conversions -const cachedNamedNodes: Record = { - ...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}. */ diff --git a/test/integration/LockingResourceStore.test.ts b/test/integration/LockingResourceStore.test.ts index 08940d168..d5c97bae8 100644 --- a/test/integration/LockingResourceStore.test.ts +++ b/test/integration/LockingResourceStore.test.ts @@ -45,7 +45,7 @@ describe('A LockingResourceStore', (): void => { // Initialize store 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)); locker = new EqualReadWriteLocker(new SingleThreadedResourceLocker()); diff --git a/test/unit/http/auxiliary/LinkMetadataGenerator.test.ts b/test/unit/http/auxiliary/LinkMetadataGenerator.test.ts index 1c5b3e140..9a09aba0a 100644 --- a/test/unit/http/auxiliary/LinkMetadataGenerator.test.ts +++ b/test/unit/http/auxiliary/LinkMetadataGenerator.test.ts @@ -1,3 +1,4 @@ +import { DataFactory } from 'n3'; import type { AuxiliaryIdentifierStrategy } from '../../../../src/http/auxiliary/AuxiliaryIdentifierStrategy'; import { LinkMetadataGenerator } from '../../../../src/http/auxiliary/LinkMetadataGenerator'; import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata'; @@ -35,7 +36,7 @@ describe('A LinkMetadataGenerator', (): void => { const metadata = new RepresentationMetadata(subjectId); await expect(generator.handle(metadata)).resolves.toBeUndefined(); expect(metadata.quads()).toHaveLength(1); - expect(metadata.get(link)?.value).toBe(auxiliaryId.path); - expect(metadata.getAll(link, SOLID_META.terms.ResponseMetadata)).toHaveLength(1); + expect(metadata.get(DataFactory.namedNode(link))?.value).toBe(auxiliaryId.path); + expect(metadata.getAll(DataFactory.namedNode(link), SOLID_META.terms.ResponseMetadata)).toHaveLength(1); }); }); diff --git a/test/unit/http/input/metadata/LinkParser.test.ts b/test/unit/http/input/metadata/LinkParser.test.ts index 76bdd4301..73be09cf9 100644 --- a/test/unit/http/input/metadata/LinkParser.test.ts +++ b/test/unit/http/input/metadata/LinkParser.test.ts @@ -22,14 +22,14 @@ describe('A LinkParser', (): void => { request.headers.link = ';rel="type"'; await expect(parser.handle({ request, metadata })).resolves.toBeUndefined(); 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 => { request.headers.link = [ ';rel="type"', ';rel=type' ]; await expect(parser.handle({ request, metadata })).resolves.toBeUndefined(); 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' ]); }); @@ -37,7 +37,7 @@ describe('A LinkParser', (): void => { request.headers.link = ';rel="type" , ;rel=type'; await expect(parser.handle({ request, metadata })).resolves.toBeUndefined(); 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' ]); }); diff --git a/test/unit/http/input/metadata/SlugParser.test.ts b/test/unit/http/input/metadata/SlugParser.test.ts index 94ce68cb3..5ae29d0ad 100644 --- a/test/unit/http/input/metadata/SlugParser.test.ts +++ b/test/unit/http/input/metadata/SlugParser.test.ts @@ -30,6 +30,6 @@ describe('A SlugParser', (): void => { request.headers.slug = 'slugA'; await expect(parser.handle({ request, metadata })).resolves.toBeUndefined(); expect(metadata.quads()).toHaveLength(1); - expect(metadata.get(SOLID_HTTP.slug)?.value).toBe('slugA'); + expect(metadata.get(SOLID_HTTP.terms.slug)?.value).toBe('slugA'); }); }); diff --git a/test/unit/http/ldp/PatchOperationHandler.test.ts b/test/unit/http/ldp/PatchOperationHandler.test.ts index fda18dcac..80a51ac8e 100644 --- a/test/unit/http/ldp/PatchOperationHandler.test.ts +++ b/test/unit/http/ldp/PatchOperationHandler.test.ts @@ -42,7 +42,7 @@ describe('A PatchOperationHandler', (): void => { expect(store.modifyResource).toHaveBeenCalledTimes(1); expect(store.modifyResource).toHaveBeenLastCalledWith(operation.target, body, conditions); 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(); }); diff --git a/test/unit/http/ldp/PostOperationHandler.test.ts b/test/unit/http/ldp/PostOperationHandler.test.ts index d75230950..f22c9a290 100644 --- a/test/unit/http/ldp/PostOperationHandler.test.ts +++ b/test/unit/http/ldp/PostOperationHandler.test.ts @@ -41,7 +41,7 @@ describe('A PostOperationHandler', (): void => { const result = await handler.handle({ operation }); expect(result.statusCode).toBe(201); 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(store.addResource).toHaveBeenCalledTimes(1); expect(store.addResource).toHaveBeenLastCalledWith(operation.target, body, conditions); diff --git a/test/unit/http/ldp/PutOperationHandler.test.ts b/test/unit/http/ldp/PutOperationHandler.test.ts index a4b00c917..00473ce24 100644 --- a/test/unit/http/ldp/PutOperationHandler.test.ts +++ b/test/unit/http/ldp/PutOperationHandler.test.ts @@ -41,7 +41,7 @@ describe('A PutOperationHandler', (): void => { expect(store.setRepresentation).toHaveBeenCalledTimes(1); expect(store.setRepresentation).toHaveBeenLastCalledWith(operation.target, body, conditions); 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(); }); diff --git a/test/unit/http/representation/RepresentationMetadata.test.ts b/test/unit/http/representation/RepresentationMetadata.test.ts index 710bb2d8d..7d82e78c1 100644 --- a/test/unit/http/representation/RepresentationMetadata.test.ts +++ b/test/unit/http/representation/RepresentationMetadata.test.ts @@ -3,7 +3,7 @@ import type { BlankNode } from 'n3'; import { DataFactory } from 'n3'; import type { NamedNode, Quad } from 'rdf-js'; 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; // Helper functions to filter quads @@ -82,14 +82,14 @@ describe('A RepresentationMetadata', (): void => { it('takes overrides for specific predicates.', async(): Promise => { 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') }); - expect(metadata.get('predVal')).toEqualRdfTerm(literal('objVal')); + expect(metadata.get(namedNode('predVal'))).toEqualRdfTerm(literal('objVal')); metadata = new RepresentationMetadata({ predVal: [ 'objVal1', literal('objVal2') ], predVal2: 'objVal3' }); - expect(metadata.getAll('predVal')).toEqualRdfTermArray([ literal('objVal1'), literal('objVal2') ]); - expect(metadata.get('predVal2')).toEqualRdfTerm(literal('objVal3')); + expect(metadata.getAll(namedNode('predVal'))).toEqualRdfTermArray([ literal('objVal1'), literal('objVal2') ]); + expect(metadata.get(namedNode('predVal2'))).toEqualRdfTerm(literal('objVal3')); }); it('can combine overrides with an identifier.', async(): Promise => { @@ -153,7 +153,7 @@ describe('A RepresentationMetadata', (): void => { // `setMetadata` should have the same result as the following const expectedMetadata = new RepresentationMetadata(identifier).addQuads(inputQuads); expectedMetadata.identifier = namedNode('otherId'); - expectedMetadata.add('test:pred', 'objVal'); + expectedMetadata.add(namedNode('test:pred'), 'objVal'); expect(metadata.identifier).toEqual(other.identifier); expect(metadata.quads()).toBeRdfIsomorphic(expectedMetadata.quads()); @@ -161,13 +161,13 @@ describe('A RepresentationMetadata', (): void => { it('can add a quad.', async(): Promise => { 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 ]); }); it('can add a quad with a graph.', async(): Promise => { 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 ]); }); @@ -186,7 +186,7 @@ describe('A RepresentationMetadata', (): void => { }); it('removes all matching triples if graph is undefined.', async(): Promise => { - metadata.removeQuad(identifier, 'has', 'data'); + metadata.removeQuad(identifier, namedNode('has'), 'data'); expect(metadata.quads()).toHaveLength(inputQuads.length - 2); 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 => { 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 => { @@ -293,15 +292,15 @@ describe('A RepresentationMetadata', (): void => { it('has a shorthand for content-type.', async(): Promise => { expect(metadata.contentType).toBeUndefined(); 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'); metadata.contentType = undefined; expect(metadata.contentType).toBeUndefined(); }); it('errors if a shorthand has multiple values.', async(): Promise => { - metadata.add(CONTENT_TYPE, 'a/b'); - metadata.add(CONTENT_TYPE, 'c/d'); + metadata.add(CONTENT_TYPE_TERM, 'a/b'); + metadata.add(CONTENT_TYPE_TERM, 'c/d'); expect((): any => metadata.contentType).toThrow(); }); diff --git a/test/unit/pods/generate/TemplatedResourcesGenerator.test.ts b/test/unit/pods/generate/TemplatedResourcesGenerator.test.ts index 0e3723f71..77684efcf 100644 --- a/test/unit/pods/generate/TemplatedResourcesGenerator.test.ts +++ b/test/unit/pods/generate/TemplatedResourcesGenerator.test.ts @@ -1,3 +1,4 @@ +import { DataFactory } from 'n3'; import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier'; import { TemplatedResourcesGenerator } from '../../../../src/pods/generate/TemplatedResourcesGenerator'; import type { @@ -10,6 +11,8 @@ import { readableToString } from '../../../../src/util/StreamUtil'; import { HandlebarsTemplateEngine } from '../../../../src/util/templates/HandlebarsTemplateEngine'; import { mockFs } from '../../../util/Util'; +const { namedNode } = DataFactory; + jest.mock('fs'); class DummyFactory implements FileIdentifierMapperFactory { @@ -115,7 +118,7 @@ describe('A TemplatedResourcesGenerator', (): void => { const rootMetadata = result[0].representation.metadata; expect(rootMetadata.identifier.value).toBe(location.path); 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'); // Container has no metadata triples besides content-type @@ -128,7 +131,7 @@ describe('A TemplatedResourcesGenerator', (): void => { const docMetadata = result[2].representation.metadata; expect(docMetadata.identifier.value).toBe(`${location.path}container/template`); 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'); }); }); diff --git a/test/unit/quota/PodQuotaStrategy.test.ts b/test/unit/quota/PodQuotaStrategy.test.ts index 64ef455b2..f8ce421a0 100644 --- a/test/unit/quota/PodQuotaStrategy.test.ts +++ b/test/unit/quota/PodQuotaStrategy.test.ts @@ -39,7 +39,7 @@ describe('PodQuotaStrategy', (): void => { async(identifier: ResourceIdentifier): Promise => { const res = new RepresentationMetadata(); if (identifier.path === `${base}nested/`) { - res.add(RDF.type, PIM.Storage); + res.add(RDF.terms.type, PIM.Storage); } return res; }, diff --git a/test/unit/storage/DataAccessorBasedStore.test.ts b/test/unit/storage/DataAccessorBasedStore.test.ts index 069c9942c..0cedc6901 100644 --- a/test/unit/storage/DataAccessorBasedStore.test.ts +++ b/test/unit/storage/DataAccessorBasedStore.test.ts @@ -181,7 +181,8 @@ describe('A DataAccessorBasedStore', (): void => { expect(result).toMatchObject({ binary: true }); expect(await arrayifyStream(result.data)).toEqual([ resourceData ]); 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 => { @@ -196,7 +197,8 @@ describe('A DataAccessorBasedStore', (): void => { expect(result).toMatchObject({ binary: false }); expect(await arrayifyStream(result.data)).toBeRdfIsomorphic(metaMirror.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 => { @@ -255,13 +257,13 @@ describe('A DataAccessorBasedStore', (): void => { it('errors when trying to create a container with non-RDF data.', async(): Promise => { 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); }); it('can write resources.', async(): Promise => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); const result = await store.addResource(resourceID, representation); expect(result).toEqual({ path: expect.stringMatching(new RegExp(`^${root}[^/]+$`, 'u')), @@ -272,7 +274,7 @@ describe('A DataAccessorBasedStore', (): void => { it('can write containers.', async(): Promise => { 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.data = guardedStreamFrom([ '<> a .' ]); 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 => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); - representation.metadata.add(SOLID_HTTP.slug, 'newName'); + representation.metadata.removeAll(RDF.terms.type); + representation.metadata.add(SOLID_HTTP.terms.slug, 'newName'); const result = await store.addResource(resourceID, representation); expect(result).toEqual({ 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 => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); - representation.metadata.add(SOLID_HTTP.slug, 'noContainer/'); + representation.metadata.removeAll(RDF.terms.type); + representation.metadata.add(SOLID_HTTP.terms.slug, 'noContainer/'); representation.data = guardedStreamFrom([ `` ]); 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 /, ' + 'but has a Link rel:type Container header.', async(): Promise => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); - representation.metadata.add(RDF.type, LDP.terms.Container); - representation.metadata.add(SOLID_HTTP.slug, 'newContainer'); + representation.metadata.removeAll(RDF.terms.type); + representation.metadata.add(RDF.terms.type, LDP.terms.Container); + representation.metadata.add(SOLID_HTTP.terms.slug, 'newContainer'); representation.data = guardedStreamFrom([ `` ]); const result = await store.addResource(resourceID, representation); 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 => { const resourceID = { path: root }; - representation.metadata.add(SOLID_HTTP.slug, 'newName'); + representation.metadata.add(SOLID_HTTP.terms.slug, 'newName'); accessor.data[`${root}newName`] = representation; const result = await store.addResource(resourceID, representation); 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 => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); - representation.metadata.add(SOLID_HTTP.slug, '&%26'); + representation.metadata.removeAll(RDF.terms.type); + representation.metadata.add(SOLID_HTTP.terms.slug, '&%26'); const result = await store.addResource(resourceID, representation); expect(result).toEqual({ path: `${root}%26%26` }); }); it('errors if the slug contains a slash.', async(): Promise => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); 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); await expect(result).rejects.toThrow(BadRequestHttpError); 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 => { const resourceID = { path: root }; - representation.metadata.removeAll(RDF.type); - representation.metadata.add(SOLID_HTTP.slug, 'test.dummy'); + representation.metadata.removeAll(RDF.terms.type); + representation.metadata.add(SOLID_HTTP.terms.slug, 'test.dummy'); const result = store.addResource(resourceID, representation); await expect(result).rejects.toThrow(ForbiddenHttpError); 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 resourceID = { path: `${root}` }; - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); representation.metadata.contentType = 'text/turtle'; representation.data = guardedStreamFrom([ `<${root}> a .` ]); @@ -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 => { 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( 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 => { 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); }); @@ -450,7 +452,7 @@ describe('A DataAccessorBasedStore', (): void => { const resourceID = { path: `${root}container/` }; // Generate based on URI - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); representation.metadata.contentType = 'text/turtle'; representation.data = guardedStreamFrom([ `<${root}resource/> a .` ]); await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([ @@ -488,15 +490,15 @@ describe('A DataAccessorBasedStore', (): void => { it('does not write generated metadata.', async(): Promise => { const resourceID = { path: `${root}resource` }; - representation.metadata.add('notGen', 'value'); - representation.metadata.add('gen', 'value', SOLID_META.terms.ResponseMetadata); + representation.metadata.add(namedNode('notGen'), 'value'); + representation.metadata.add(namedNode('gen'), 'value', SOLID_META.terms.ResponseMetadata); await expect(store.setRepresentation(resourceID, representation)).resolves.toEqual([ { path: root }, { path: `${root}resource` }, ]); 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('gen')).toBeUndefined(); + expect(accessor.data[resourceID.path].metadata.get(namedNode('notGen'))?.value).toBe('value'); + expect(accessor.data[resourceID.path].metadata.get(namedNode('gen'))).toBeUndefined(); }); it('can write resources even if root does not exist.', async(): Promise => { @@ -514,7 +516,7 @@ describe('A DataAccessorBasedStore', (): void => { const resourceID = { path: `${root}container/` }; // Generate based on URI - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); representation.metadata.contentType = 'internal/quads'; representation.data = guardedStreamFrom( [ 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 => { 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.identifier = DataFactory.namedNode(`${root}resource/`); representation.data = guardedStreamFrom( @@ -548,9 +550,9 @@ describe('A DataAccessorBasedStore', (): void => { { path: `${root}a/b/resource` }, ]); 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); - 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); }); @@ -568,7 +570,7 @@ describe('A DataAccessorBasedStore', (): void => { const resourceID = { path: `${root}` }; // Generate based on URI - representation.metadata.removeAll(RDF.type); + representation.metadata.removeAll(RDF.terms.type); representation.metadata.contentType = 'text/turtle'; representation.data = guardedStreamFrom([]); 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 => { - representation.metadata.add(RDF.type, PIM.terms.Storage); + representation.metadata.add(RDF.terms.type, PIM.terms.Storage); accessor.data[`${root}container/`] = representation; const result = store.deleteResource({ path: `${root}container/` }); 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 => { 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/.dummy`] = representation; auxiliaryStrategy.isRequiredInRoot = jest.fn().mockReturnValue(true); diff --git a/test/unit/storage/accessors/FileDataAccessor.test.ts b/test/unit/storage/accessors/FileDataAccessor.test.ts index 51d3ce687..221977b5f 100644 --- a/test/unit/storage/accessors/FileDataAccessor.test.ts +++ b/test/unit/storage/accessors/FileDataAccessor.test.ts @@ -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 { mockFs } from '../../../util/Util'; +const { namedNode } = DataFactory; + jest.mock('fs'); const rootFilePath = 'uploads'; @@ -104,10 +106,11 @@ describe('A FileDataAccessor', (): void => { metadata = await accessor.getMetadata({ path: `${base}resource.ttl` }); expect(metadata.identifier.value).toBe(`${base}resource.ttl`); expect(metadata.contentType).toBe('text/turtle'); - expect(metadata.get(RDF.type)?.value).toBe(LDP.Resource); - expect(metadata.get(POSIX.size)).toEqualRdfTerm(toLiteral('data'.length, XSD.terms.integer)); - expect(metadata.get(DC.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(RDF.terms.type)?.value).toBe(LDP.Resource); + expect(metadata.get(POSIX.terms.size)).toEqualRdfTerm(toLiteral('data'.length, XSD.terms.integer)); + expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); + expect(metadata.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), + XSD.terms.integer)); // `dc:modified` is in the default graph 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 => { cache.data = { container: {}}; metadata = await accessor.getMetadata({ path: `${base}container/` }); - expect(metadata.get(POSIX.size)).toBeUndefined(); - expect(metadata.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); + expect(metadata.get(POSIX.terms.size)).toBeUndefined(); + expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); }); it('generates the metadata for a container.', async(): Promise => { @@ -130,12 +133,13 @@ describe('A FileDataAccessor', (): void => { }; metadata = await accessor.getMetadata({ path: `${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 ], ); - expect(metadata.get(POSIX.size)).toBeUndefined(); - expect(metadata.get(DC.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.size)).toBeUndefined(); + expect(metadata.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); + expect(metadata.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), + XSD.terms.integer)); // `dc:modified` is in the default graph expect(metadata.quads(null, null, null, SOLID_META.terms.ResponseMetadata)).toHaveLength(1); }); @@ -169,7 +173,7 @@ describe('A FileDataAccessor', (): void => { // Containers 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.Container); expect(types).toContain(LDP.BasicContainer); @@ -177,7 +181,7 @@ describe('A FileDataAccessor', (): void => { // Documents 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('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource'); expect(types).not.toContain(LDP.Container); @@ -186,8 +190,8 @@ describe('A FileDataAccessor', (): void => { // All resources for (const child of children) { - expect(child.get(DC.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); - expect(child.get(POSIX.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), + expect(child.get(DC.terms.modified)).toEqualRdfTerm(toLiteral(now.toISOString(), XSD.terms.dateTime)); + expect(child.get(POSIX.terms.mtime)).toEqualRdfTerm(toLiteral(Math.floor(now.getTime() / 1000), XSD.terms.integer)); // `dc:modified` is in the default graph expect(child.quads(null, null, null, SOLID_META.terms.ResponseMetadata)) @@ -228,8 +232,8 @@ describe('A FileDataAccessor', (): void => { `${base}container/resource2`, ])); - const types1 = children[0].getAll(RDF.type).map((term): string => term.value); - const types2 = children[1].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.terms.type).map((term): string => term.value); expect(types1).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource'); 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 => { - 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(); expect(cache.data.resource).toBe('data'); expect(cache.data['resource.meta']).toBeUndefined(); @@ -315,7 +319,7 @@ describe('A FileDataAccessor', (): void => { data.emit('error', new Error('error')); return null; }; - metadata.add('likes', 'apples'); + metadata.add(namedNode('likes'), 'apples'); await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata)) .rejects.toThrow('error'); expect(cache.data['resource.meta']).toBeUndefined(); @@ -325,7 +329,7 @@ describe('A FileDataAccessor', (): void => { cache.data = { 'resource$.ttl': ' .', 'resource.meta': ' .' }; metadata.identifier = DataFactory.namedNode(`${base}resource`); metadata.contentType = 'text/plain'; - metadata.add('new', 'metadata'); + metadata.add(namedNode('new'), 'metadata'); await expect(accessor.writeDocument({ path: `${base}resource` }, data, metadata)) .resolves.toBeUndefined(); 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 => { metadata.identifier = DataFactory.namedNode(`${base}resource.txt`); metadata.contentType = 'text/turtle'; - metadata.add('new', 'metadata'); + metadata.add(namedNode('new'), 'metadata'); await expect(accessor.writeDocument({ path: `${base}resource.txt` }, data, metadata)) .resolves.toBeUndefined(); expect(cache.data).toEqual({ diff --git a/test/unit/storage/accessors/InMemoryDataAccessor.test.ts b/test/unit/storage/accessors/InMemoryDataAccessor.test.ts index 20c4c624b..73b84d30f 100644 --- a/test/unit/storage/accessors/InMemoryDataAccessor.test.ts +++ b/test/unit/storage/accessors/InMemoryDataAccessor.test.ts @@ -133,14 +133,14 @@ describe('An InMemoryDataAccessor', (): void => { )).resolves.toBeUndefined(); 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(); metadata = await accessor.getMetadata(identifier); expect(metadata.identifier.value).toBe(`${base}container/`); const quads = metadata.quads(); 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 ]); const children = []; @@ -168,7 +168,7 @@ describe('An InMemoryDataAccessor', (): void => { expect(metadata.identifier.value).toBe(`${base}`); const quads = metadata.quads(); expect(quads).toHaveLength(1); - expect(metadata.getAll(RDF.type)).toHaveLength(1); + expect(metadata.getAll(RDF.terms.type)).toHaveLength(1); const children = []; for await (const child of accessor.getChildren(identifier)) { diff --git a/test/unit/storage/conversion/ConstantConverter.test.ts b/test/unit/storage/conversion/ConstantConverter.test.ts index dd48dfeab..d634fd178 100644 --- a/test/unit/storage/conversion/ConstantConverter.test.ts +++ b/test/unit/storage/conversion/ConstantConverter.test.ts @@ -63,7 +63,7 @@ describe('A ConstantConverter', (): void => { it('does not support representations that are already in the right format.', async(): Promise => { 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 args = { identifier, representation, preferences }; @@ -101,7 +101,7 @@ describe('A ConstantConverter', (): void => { it('replaces the representation of a supported request.', async(): Promise => { 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 args = { identifier, representation, preferences }; diff --git a/test/unit/storage/conversion/ContentTypeReplacer.test.ts b/test/unit/storage/conversion/ContentTypeReplacer.test.ts index 77768d332..5d242e56d 100644 --- a/test/unit/storage/conversion/ContentTypeReplacer.test.ts +++ b/test/unit/storage/conversion/ContentTypeReplacer.test.ts @@ -2,6 +2,7 @@ import 'jest-rdf'; import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata'; import { ContentTypeReplacer } from '../../../../src/storage/conversion/ContentTypeReplacer'; import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; +import { CONTENT_TYPE } from '../../../../src/util/Vocabularies'; const binary = true; const data = { data: true }; @@ -21,7 +22,7 @@ describe('A ContentTypeReplacer', (): void => { }); it('throws on an unsupported input type.', async(): Promise => { - const metadata = new RepresentationMetadata({ contentType: 'text/plain' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }); const representation = { metadata }; const preferences = { type: { 'application/json': 1 }}; @@ -31,7 +32,7 @@ describe('A ContentTypeReplacer', (): void => { }); it('throws on an unsupported output type.', async(): Promise => { - const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' }); const representation = { metadata }; 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 => { - const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' }); const representation = { binary, data, metadata }; const preferences = {}; @@ -62,7 +63,7 @@ describe('A ContentTypeReplacer', (): void => { }); it('replaces a supported content type when preferences are given.', async(): Promise => { - const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' }); const representation = { binary, data, metadata }; const preferences = { type: { 'application/n-quads': 1 }}; @@ -73,7 +74,7 @@ describe('A ContentTypeReplacer', (): void => { }); it('replaces a supported wildcard type.', async(): Promise => { - const metadata = new RepresentationMetadata({ contentType: 'text/plain' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }); const representation = { binary, data, metadata }; const preferences = { type: { 'application/octet-stream': 1 }}; @@ -84,7 +85,7 @@ describe('A ContentTypeReplacer', (): void => { }); it('picks the most preferred content type.', async(): Promise => { - const metadata = new RepresentationMetadata({ contentType: 'application/n-triples' }); + const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/n-triples' }); const representation = { binary, data, metadata }; const preferences = { type: { 'text/turtle': 0.5, diff --git a/test/unit/util/TermUtil.test.ts b/test/unit/util/TermUtil.test.ts index 615a93b94..9fc3ccd8c 100644 --- a/test/unit/util/TermUtil.test.ts +++ b/test/unit/util/TermUtil.test.ts @@ -1,14 +1,13 @@ import 'jest-rdf'; import { DataFactory } from 'n3'; import { - toCachedNamedNode, toNamedTerm, toPredicateTerm, toObjectTerm, toLiteral, isTerm, } from '../../../src/util/TermUtil'; -import { CONTENT_TYPE_TERM, XSD } from '../../../src/util/Vocabularies'; +import { XSD } from '../../../src/util/Vocabularies'; const { literal, namedNode } = DataFactory; 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 => { - const term = namedNode('name'); - expect(toCachedNamedNode(term)).toBe(term); - }); - - it('returns a named node when a string is used.', async(): Promise => { - expect(toCachedNamedNode('name')).toEqualRdfTerm(namedNode('name')); - }); - - it('caches generated named nodes.', async(): Promise => { - const result = toCachedNamedNode('name'); - expect(result).toEqualRdfTerm(namedNode('name')); - expect(toCachedNamedNode('name')).toBe(result); - }); - - it('supports URI shorthands.', async(): Promise => { - expect(toCachedNamedNode('contentType')).toEqualRdfTerm(CONTENT_TYPE_TERM); - }); - }); - describe('toSubjectTerm function', (): void => { it('returns the input if it was a term.', async(): Promise => { const nn = namedNode('name');