diff --git a/src/authorization/WebAclAuthorizer.ts b/src/authorization/WebAclAuthorizer.ts index dd8bd7720..f464c428d 100644 --- a/src/authorization/WebAclAuthorizer.ts +++ b/src/authorization/WebAclAuthorizer.ts @@ -122,7 +122,7 @@ export class WebAclAuthorizer extends Authorizer { try { const acl = await this.aclManager.getAclDocument(id); this.logger.debug(`Trying to read the ACL document ${acl.path}`); - const data = await this.resourceStore.getRepresentation(acl, { type: [{ value: INTERNAL_QUADS, weight: 1 }]}); + const data = await this.resourceStore.getRepresentation(acl, { type: { [INTERNAL_QUADS]: 1 }}); this.logger.info(`Reading ACL statements from ${acl.path}`); const resourceId = await this.aclManager.getAclConstrainedResource(id); diff --git a/src/ldp/http/AcceptPreferenceParser.ts b/src/ldp/http/AcceptPreferenceParser.ts index 7756200b2..f1bd64c10 100644 --- a/src/ldp/http/AcceptPreferenceParser.ts +++ b/src/ldp/http/AcceptPreferenceParser.ts @@ -6,7 +6,6 @@ import { parseAcceptEncoding, parseAcceptLanguage, } from '../../util/HeaderUtil'; -import type { RepresentationPreference } from '../representation/RepresentationPreference'; import type { RepresentationPreferences } from '../representation/RepresentationPreferences'; import { PreferenceParser } from './PreferenceParser'; @@ -31,13 +30,13 @@ export class AcceptPreferenceParser extends PreferenceParser { (Object.keys(headers) as (keyof RepresentationPreferences)[]).forEach((key): void => { const preferences = this.parseHeader(headers[key]!.func, headers[key]!.val); if (preferences.length > 0) { - result[key] = preferences; + result[key] = Object.fromEntries(preferences); } }); // Accept-DateTime is currently specified to simply have a datetime as value if (input.headers['accept-datetime']) { - result.datetime = [{ value: input.headers['accept-datetime'] as string, weight: 1 }]; + result.datetime = { [input.headers['accept-datetime'] as string]: 1 }; } return result; @@ -48,13 +47,10 @@ export class AcceptPreferenceParser extends PreferenceParser { * @param input - Input header string. * @param parseFunction - Function that converts header string to {@link AcceptHeader}. * - * @returns A list of {@link RepresentationPreference}. Returns an empty list if input was not defined. + * @returns A list of preferences. Returns an empty list if input was not defined. */ - private parseHeader(parseFunction: (input: string) => AcceptHeader[], input?: string): RepresentationPreference[] { - if (!input) { - return []; - } - return parseFunction(input).map((accept): RepresentationPreference => - ({ value: accept.range, weight: accept.weight })); + private parseHeader(parseFunction: (input: string) => AcceptHeader[], input?: string): [string, number][] { + return (input ? parseFunction(input) : []) + .map(({ range, weight }): [string, number] => [ range, weight ]); } } diff --git a/src/ldp/representation/RepresentationPreference.ts b/src/ldp/representation/RepresentationPreference.ts index 880899745..a8ce0e117 100644 --- a/src/ldp/representation/RepresentationPreference.ts +++ b/src/ldp/representation/RepresentationPreference.ts @@ -1,18 +1,10 @@ /** - * Represents a single preference in a request. - */ -export interface RepresentationPreference { - /** - * The actual preference value. - */ - value: string; - /** - * How preferred this value is in a number going from 0 to 1. - * Follows the quality values rule from RFC 7231: - * - * "The weight is normalized to a real number in the range 0 through 1, - * where 0.001 is the least preferred and 1 is the most preferred; a - * value of 0 means "not acceptable"." - */ - weight: number; -} + * Represents preferred values along a single content negotiation dimension. + * + * The number represents how preferred this value is from 0 to 1. + * Follows the quality values rule from RFC 7231: + * "The weight is normalized to a real number in the range 0 through 1, + * where 0.001 is the least preferred and 1 is the most preferred; a + * value of 0 means "not acceptable"." + */ +export type RepresentationPreference = Record; diff --git a/src/ldp/representation/RepresentationPreferences.ts b/src/ldp/representation/RepresentationPreferences.ts index 83aec3109..02d5e593c 100644 --- a/src/ldp/representation/RepresentationPreferences.ts +++ b/src/ldp/representation/RepresentationPreferences.ts @@ -4,9 +4,9 @@ import type { RepresentationPreference } from './RepresentationPreference'; * Contains the preferences of which kind of representation is requested. */ export interface RepresentationPreferences { - type?: RepresentationPreference[]; - charset?: RepresentationPreference[]; - datetime?: RepresentationPreference[]; - encoding?: RepresentationPreference[]; - language?: RepresentationPreference[]; + type?: RepresentationPreference; + charset?: RepresentationPreference; + datetime?: RepresentationPreference; + encoding?: RepresentationPreference; + language?: RepresentationPreference; } diff --git a/src/storage/RepresentationConvertingStore.ts b/src/storage/RepresentationConvertingStore.ts index 39e577d3a..cb3ec2b61 100644 --- a/src/storage/RepresentationConvertingStore.ts +++ b/src/storage/RepresentationConvertingStore.ts @@ -90,8 +90,8 @@ export class RepresentationConvertingStore `${pref.value};q=${pref.weight}`).join(', ')}`); + .path} from ${input.representation.metadata.contentType} to satisfy ${Object.entries(input.preferences.type) + .map(([ value, weight ]): string => `${value};q=${weight}`).join(', ')}`); const converted = await converter.handleSafe(input); this.logger.info(`Converted representation for ${input.identifier @@ -107,7 +107,7 @@ export class RepresentationConvertingStore { - if (!Array.isArray(preferences.type)) { +export const matchingMediaTypes = (preferences: RepresentationPreferences, available: string[]): +string[] => { + const preferredTypes = preferences.type; + if (!preferredTypes || Object.keys(preferredTypes).length === 0) { throw new BadRequestHttpError('Output type required for conversion.'); } - const prefMap = preferences.type.reduce((map: Record, pref): Record => { - if (map[pref.value]) { - throw new BadRequestHttpError(`Duplicate type preference found: ${pref.value}`); - } - map[pref.value] = pref.weight; - return map; - }, {}); - // Prevent accidental use of internal types - if (!prefMap[INTERNAL_ALL]) { - prefMap[INTERNAL_ALL] = 0; + if (!preferredTypes[INTERNAL_ALL]) { + preferredTypes[INTERNAL_ALL] = 0; } // RFC 7231 // Media ranges can be overridden by more specific media ranges or // specific media types. If more than one media range applies to a // given type, the most specific reference has precedence. - const weightedSupported = types.map((type): RepresentationPreference => { + const weightedSupported = available.map((type): [string, number] => { const match = /^([^/]+)\/([^\s;]+)/u.exec(type); if (!match) { throw new InternalServerError(`Unexpected type preference: ${type}`); } const [ , main, sub ] = match; - const weight = prefMap[type] ?? prefMap[`${main}/${sub}`] ?? prefMap[`${main}/*`] ?? prefMap['*/*'] ?? 0; - return { value: type, weight }; + const weight = + preferredTypes[type] ?? + preferredTypes[`${main}/${sub}`] ?? + preferredTypes[`${main}/*`] ?? + preferredTypes['*/*'] ?? + 0; + return [ type, weight ]; }); // Return all non-zero preferences in descending order of weight return weightedSupported - .filter((pref): boolean => pref.weight !== 0) - .sort((prefA, prefB): number => prefB.weight - prefA.weight); + .filter(([ , weight ]): boolean => weight !== 0) + .sort(([ , weightA ], [ , weightB ]): number => weightB - weightA) + .map(([ type ]): string => type); }; /** diff --git a/src/storage/conversion/QuadToRdfConverter.ts b/src/storage/conversion/QuadToRdfConverter.ts index 35e4ccf7f..eb61fb4b1 100644 --- a/src/storage/conversion/QuadToRdfConverter.ts +++ b/src/storage/conversion/QuadToRdfConverter.ts @@ -2,6 +2,7 @@ import type { Readable } from 'stream'; import rdfSerializer from 'rdf-serialize'; import type { Representation } from '../../ldp/representation/Representation'; import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata'; +import type { RepresentationPreference } from '../../ldp/representation/RepresentationPreference'; import type { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences'; import { INTERNAL_QUADS } from '../../util/ContentTypes'; import { guardStream } from '../../util/GuardedStream'; @@ -14,11 +15,11 @@ import { TypedRepresentationConverter } from './TypedRepresentationConverter'; * Converts `internal/quads` to most major RDF serializations. */ export class QuadToRdfConverter extends TypedRepresentationConverter { - public async getInputTypes(): Promise> { + public async getInputTypes(): Promise { return { [INTERNAL_QUADS]: 1 }; } - public async getOutputTypes(): Promise> { + public async getOutputTypes(): Promise { return rdfSerializer.getContentTypesPrioritized(); } @@ -27,7 +28,7 @@ export class QuadToRdfConverter extends TypedRepresentationConverter { } private async quadsToRdf(quads: Representation, preferences: RepresentationPreferences): Promise { - const contentType = matchingMediaTypes(preferences, await rdfSerializer.getContentTypes())[0].value; + const contentType = matchingMediaTypes(preferences, await rdfSerializer.getContentTypes())[0]; const metadata = new RepresentationMetadata(quads.metadata, { [CONTENT_TYPE]: contentType }); return { binary: true, diff --git a/src/storage/conversion/TypedRepresentationConverter.ts b/src/storage/conversion/TypedRepresentationConverter.ts index 850667cdc..31c8ed045 100644 --- a/src/storage/conversion/TypedRepresentationConverter.ts +++ b/src/storage/conversion/TypedRepresentationConverter.ts @@ -1,3 +1,4 @@ +import type { RepresentationPreference } from '../../ldp/representation/RepresentationPreference'; import { supportsConversion } from './ConversionUtil'; import { RepresentationConverter } from './RepresentationConverter'; import type { RepresentationConverterArgs } from './RepresentationConverter'; @@ -7,18 +8,14 @@ import type { RepresentationConverterArgs } from './RepresentationConverter'; */ export abstract class TypedRepresentationConverter extends RepresentationConverter { /** - * Get a hash of all supported input content types for this converter, mapped to a numerical priority. - * The priority weight goes from 0 up to 1. - * @returns A promise resolving to a hash mapping content type to a priority number. + * Gets the supported input content types for this converter, mapped to a numerical priority. */ - public abstract getInputTypes(): Promise>; + public abstract getInputTypes(): Promise; /** - * Get a hash of all supported output content types for this converter, mapped to a numerical priority. - * The priority weight goes from 0 up to 1. - * @returns A promise resolving to a hash mapping content type to a priority number. + * Gets the supported output content types for this converter, mapped to a numerical quality. */ - public abstract getOutputTypes(): Promise>; + public abstract getOutputTypes(): Promise; /** * Verifies whether this converter supports the input. diff --git a/src/storage/patch/SparqlUpdatePatchHandler.ts b/src/storage/patch/SparqlUpdatePatchHandler.ts index 6f08aa5be..aac91356a 100644 --- a/src/storage/patch/SparqlUpdatePatchHandler.ts +++ b/src/storage/patch/SparqlUpdatePatchHandler.ts @@ -86,7 +86,8 @@ export class SparqlUpdatePatchHandler extends PatchHandler { const store = new Store(); try { // Read the quads of the current representation - const quads = await this.source.getRepresentation(identifier, { type: [{ value: INTERNAL_QUADS, weight: 1 }]}); + const quads = await this.source.getRepresentation(identifier, + { type: { [INTERNAL_QUADS]: 1 }}); const importEmitter = store.import(quads.data); await new Promise((resolve, reject): void => { importEmitter.on('end', resolve); diff --git a/src/storage/routing/PreferenceSupport.ts b/src/storage/routing/PreferenceSupport.ts index 164834631..4aa1d6ca1 100644 --- a/src/storage/routing/PreferenceSupport.ts +++ b/src/storage/routing/PreferenceSupport.ts @@ -15,7 +15,7 @@ export class PreferenceSupport { private readonly converter: RepresentationConverter; public constructor(type: string, converter: RepresentationConverter) { - this.preferences = { type: [{ value: type, weight: 1 }]}; + this.preferences = { type: { [type]: 1 }}; this.converter = converter; } diff --git a/test/integration/RepresentationConverter.test.ts b/test/integration/RepresentationConverter.test.ts index bbfdec707..cbf144ea5 100644 --- a/test/integration/RepresentationConverter.test.ts +++ b/test/integration/RepresentationConverter.test.ts @@ -25,7 +25,7 @@ describe('A ChainedConverter', (): void => { const result = await converter.handleSafe({ representation, - preferences: { type: [{ value: 'text/turtle', weight: 1 }]}, + preferences: { type: { 'text/turtle': 1 }}, identifier: { path: 'path' }, }); @@ -43,7 +43,7 @@ describe('A ChainedConverter', (): void => { const result = await converter.handleSafe({ representation, - preferences: { type: [{ value: 'application/ld+json', weight: 1 }]}, + preferences: { type: { 'application/ld+json': 1 }}, identifier: { path: 'path' }, }); diff --git a/test/integration/RequestParser.test.ts b/test/integration/RequestParser.test.ts index 9db393c33..d5a10c95c 100644 --- a/test/integration/RequestParser.test.ts +++ b/test/integration/RequestParser.test.ts @@ -34,8 +34,8 @@ describe('A BasicRequestParser with simple input parsers', (): void => { method: 'POST', target: { path: 'http://test.com/' }, preferences: { - type: [{ value: 'text/turtle', weight: 0.8 }], - language: [{ value: 'en-gb', weight: 1 }, { value: 'en', weight: 0.5 }], + type: { 'text/turtle': 0.8 }, + language: { 'en-gb': 1, en: 0.5 }, }, body: { data: expect.any(Readable), diff --git a/test/unit/ldp/http/AcceptPreferenceParser.test.ts b/test/unit/ldp/http/AcceptPreferenceParser.test.ts index 0a013e262..d47068a73 100644 --- a/test/unit/ldp/http/AcceptPreferenceParser.test.ts +++ b/test/unit/ldp/http/AcceptPreferenceParser.test.ts @@ -14,33 +14,30 @@ describe('An AcceptPreferenceParser', (): void => { it('parses accept headers.', async(): Promise => { await expect(preferenceParser.handle({ headers: { accept: 'audio/*; q=0.2, audio/basic' }} as HttpRequest)) - .resolves.toEqual({ type: [{ value: 'audio/basic', weight: 1 }, { value: 'audio/*', weight: 0.2 }]}); + .resolves.toEqual({ type: { 'audio/basic': 1, 'audio/*': 0.2 }}); }); it('parses accept-charset headers.', async(): Promise => { await expect(preferenceParser.handle( { headers: { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' }} as unknown as HttpRequest, - )).resolves.toEqual({ charset: [{ value: 'iso-8859-5', weight: 1 }, { value: 'unicode-1-1', weight: 0.8 }]}); + )).resolves.toEqual({ charset: { 'iso-8859-5': 1, 'unicode-1-1': 0.8 }}); }); it('parses accept-datetime headers.', async(): Promise => { await expect(preferenceParser.handle( { headers: { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' }} as unknown as HttpRequest, - )).resolves.toEqual({ datetime: [{ value: 'Tue, 20 Mar 2001 20:35:00 GMT', weight: 1 }]}); + // eslint-disable-next-line @typescript-eslint/naming-convention + )).resolves.toEqual({ datetime: { 'Tue, 20 Mar 2001 20:35:00 GMT': 1 }}); }); it('parses accept-encoding headers.', async(): Promise => { await expect(preferenceParser.handle( { headers: { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' }} as unknown as HttpRequest, - )).resolves.toEqual( - { encoding: [{ value: 'gzip', weight: 1 }, { value: 'identity', weight: 0.5 }, { value: '*', weight: 0 }]}, - ); + )).resolves.toEqual({ encoding: { gzip: 1, identity: 0.5, '*': 0 }}); }); it('parses accept-language headers.', async(): Promise => { await expect(preferenceParser.handle({ headers: { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' }} as HttpRequest)) - .resolves.toEqual( - { language: [{ value: 'da', weight: 1 }, { value: 'en-gb', weight: 0.8 }, { value: 'en', weight: 0.7 }]}, - ); + .resolves.toEqual({ language: { da: 1, 'en-gb': 0.8, en: 0.7 }}); }); }); diff --git a/test/unit/storage/RepresentationConvertingStore.test.ts b/test/unit/storage/RepresentationConvertingStore.test.ts index fd25be264..93667ef0a 100644 --- a/test/unit/storage/RepresentationConvertingStore.test.ts +++ b/test/unit/storage/RepresentationConvertingStore.test.ts @@ -32,9 +32,8 @@ describe('A RepresentationConvertingStore', (): void => { }); it('returns the Representation from the source if no changes are required.', async(): Promise => { - const result = await store.getRepresentation({ path: 'path' }, { type: [ - { value: 'application/*', weight: 0 }, { value: 'text/turtle', weight: 1 }, - ]}); + const result = await store.getRepresentation({ path: 'path' }, + { type: { 'application/*': 0, 'text/turtle': 1 }}); expect(result).toEqual({ data: 'data', metadata: expect.any(RepresentationMetadata), @@ -43,7 +42,7 @@ describe('A RepresentationConvertingStore', (): void => { expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(source.getRepresentation).toHaveBeenLastCalledWith( { path: 'path' }, - { type: [{ value: 'application/*', weight: 0 }, { value: 'text/turtle', weight: 1 }]}, + { type: { 'application/*': 0, 'text/turtle': 1, 'internal/*': 0 }}, undefined, ); expect(outConverter.handleSafe).toHaveBeenCalledTimes(0); @@ -64,15 +63,14 @@ describe('A RepresentationConvertingStore', (): void => { }); it('calls the converter if another output is preferred.', async(): Promise => { - await expect(store.getRepresentation({ path: 'path' }, { type: [ - { value: 'text/plain', weight: 1 }, { value: 'text/turtle', weight: 0 }, - ]})).resolves.toEqual(convertedOut); + await expect(store.getRepresentation({ path: 'path' }, + { type: { 'text/plain': 1, 'text/turtle': 0 }})).resolves.toEqual(convertedOut); expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(outConverter.handleSafe).toHaveBeenCalledTimes(1); expect(outConverter.handleSafe).toHaveBeenLastCalledWith({ identifier: { path: 'path' }, representation: { data: 'data', metadata }, - preferences: { type: [{ value: 'text/plain', weight: 1 }, { value: 'text/turtle', weight: 0 }]}, + preferences: { type: { 'text/plain': 1, 'text/turtle': 0, 'internal/*': 0 }}, }); }); diff --git a/test/unit/storage/conversion/ChainedConverter.test.ts b/test/unit/storage/conversion/ChainedConverter.test.ts index c96104e76..f3cba3309 100644 --- a/test/unit/storage/conversion/ChainedConverter.test.ts +++ b/test/unit/storage/conversion/ChainedConverter.test.ts @@ -31,7 +31,7 @@ class DummyConverter extends TypedRepresentationConverter { public async handle(input: RepresentationConverterArgs): Promise { const metadata = new RepresentationMetadata(input.representation.metadata, - { [CONTENT_TYPE]: input.preferences.type![0].value }); + { [CONTENT_TYPE]: Object.keys(input.preferences.type!)[0] }); return { ...input.representation, metadata }; } } @@ -53,7 +53,7 @@ describe('A ChainedConverter', (): void => { const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/turtle' }); representation = { metadata } as Representation; - preferences = { type: [{ value: 'internal/quads', weight: 1 }]}; + preferences = { type: { 'internal/quads': 1 }}; args = { representation, preferences, identifier: { path: 'path' }}; }); diff --git a/test/unit/storage/conversion/ConversionUtil.test.ts b/test/unit/storage/conversion/ConversionUtil.test.ts index 7981443df..3399c1898 100644 --- a/test/unit/storage/conversion/ConversionUtil.test.ts +++ b/test/unit/storage/conversion/ConversionUtil.test.ts @@ -7,7 +7,6 @@ import { matchingMediaTypes, supportsConversion, } from '../../../../src/storage/conversion/ConversionUtil'; -import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError'; import { InternalServerError } from '../../../../src/util/errors/InternalServerError'; describe('ConversionUtil', (): void => { @@ -29,21 +28,24 @@ describe('ConversionUtil', (): void => { it('requires a matching input type.', async(): Promise => { metadata.contentType = 'a/x'; - const preferences: RepresentationPreferences = { type: [{ value: 'b/x', weight: 1 }]}; + const preferences: RepresentationPreferences = + { type: { 'b/x': 1 }}; expect((): any => supportsConversion({ identifier, representation, preferences }, [ 'c/x' ], [ 'a/x' ])) .toThrow('Can only convert from c/x to a/x.'); }); it('requires a matching output type.', async(): Promise => { metadata.contentType = 'a/x'; - const preferences: RepresentationPreferences = { type: [{ value: 'b/x', weight: 1 }]}; + const preferences: RepresentationPreferences = + { type: { 'b/x': 1 }}; expect((): any => supportsConversion({ identifier, representation, preferences }, [ 'a/x' ], [ 'c/x' ])) .toThrow('Can only convert from a/x to c/x.'); }); it('succeeds with a valid input and output type.', async(): Promise => { metadata.contentType = 'a/x'; - const preferences: RepresentationPreferences = { type: [{ value: 'b/x', weight: 1 }]}; + const preferences: RepresentationPreferences = + { type: { 'b/x': 1 }}; expect(supportsConversion({ identifier, representation, preferences }, [ 'a/x' ], [ 'b/x' ])) .toBeUndefined(); }); @@ -51,55 +53,52 @@ describe('ConversionUtil', (): void => { describe('#matchingMediaTypes', (): void => { it('requires type preferences.', async(): Promise => { - const preferences: RepresentationPreferences = {}; + const preferences: RepresentationPreferences = + {}; expect((): any => matchingMediaTypes(preferences, [ 'a/b' ])) .toThrow('Output type required for conversion.'); }); it('returns matching types if weight > 0.', async(): Promise => { - const preferences: RepresentationPreferences = { type: - [{ value: 'a/x', weight: 1 }, { value: 'b/x', weight: 0.5 }, { value: 'c/x', weight: 0 }]}; - expect(matchingMediaTypes(preferences, [ 'b/x', 'c/x' ])).toEqual([{ value: 'b/x', weight: 0.5 }]); + const preferences: RepresentationPreferences = + { type: { 'a/x': 1, 'b/x': 0.5, 'c/x': 0 }}; + expect(matchingMediaTypes(preferences, [ 'b/x', 'c/x' ])) + .toEqual([ 'b/x' ]); }); it('sorts by descending weight.', async(): Promise => { - const preferences: RepresentationPreferences = { type: - [{ value: 'a/x', weight: 1 }, { value: 'b/x', weight: 0.5 }, { value: 'c/x', weight: 0.8 }]}; - expect(matchingMediaTypes(preferences, [ 'a/x', 'b/x', 'c/x' ])) - .toEqual([{ value: 'a/x', weight: 1 }, { value: 'c/x', weight: 0.8 }, { value: 'b/x', weight: 0.5 }]); - }); - - it('errors if there are duplicate preferences.', async(): Promise => { const preferences: RepresentationPreferences = - { type: [{ value: 'b/x', weight: 1 }, { value: 'b/x', weight: 0 }]}; - expect((): any => matchingMediaTypes(preferences, [ 'b/x' ])) - .toThrow(new BadRequestHttpError(`Duplicate type preference found: b/x`)); + { type: { 'a/x': 1, 'b/x': 0.5, 'c/x': 0.8 }}; + expect(matchingMediaTypes(preferences, [ 'a/x', 'b/x', 'c/x' ])) + .toEqual([ 'a/x', 'c/x', 'b/x' ]); }); it('errors if there invalid types.', async(): Promise => { const preferences: RepresentationPreferences = - { type: [{ value: 'b/x', weight: 1 }]}; + { type: { 'b/x': 1 }}; expect((): any => matchingMediaTypes(preferences, [ 'noType' ])) .toThrow(new InternalServerError(`Unexpected type preference: noType`)); }); it('filters out internal types.', async(): Promise => { - const preferences: RepresentationPreferences = { type: [{ value: '*/*', weight: 1 }]}; - expect(matchingMediaTypes(preferences, [ 'a/x', 'internal/quads' ])).toEqual([{ value: 'a/x', weight: 1 }]); + const preferences: RepresentationPreferences = + { type: { '*/*': 1 }}; + expect(matchingMediaTypes(preferences, [ 'a/x', 'internal/quads' ])) + .toEqual([ 'a/x' ]); }); it('keeps internal types that are specifically requested.', async(): Promise => { const preferences: RepresentationPreferences = - { type: [{ value: '*/*', weight: 1 }, { value: 'internal/*', weight: 0.5 }]}; + { type: { '*/*': 1, 'internal/*': 0.5 }}; expect(matchingMediaTypes(preferences, [ 'a/x', 'internal/quads' ])) - .toEqual([{ value: 'a/x', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]); + .toEqual([ 'a/x', 'internal/quads' ]); }); it('takes the most relevant weight for a type.', async(): Promise => { const preferences: RepresentationPreferences = - { type: [{ value: '*/*', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]}; + { type: { '*/*': 1, 'internal/quads': 0.5 }}; expect(matchingMediaTypes(preferences, [ 'a/x', 'internal/quads' ])) - .toEqual([{ value: 'a/x', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]); + .toEqual([ 'a/x', 'internal/quads' ]); }); }); diff --git a/test/unit/storage/conversion/QuadToRdfConverter.test.ts b/test/unit/storage/conversion/QuadToRdfConverter.test.ts index 6da6632d4..4d2fe3be4 100644 --- a/test/unit/storage/conversion/QuadToRdfConverter.test.ts +++ b/test/unit/storage/conversion/QuadToRdfConverter.test.ts @@ -25,13 +25,13 @@ describe('A QuadToRdfConverter', (): void => { it('can handle quad to turtle conversions.', async(): Promise => { const representation = { metadata } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: 'text/turtle', weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }}; await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); }); it('can handle quad to JSON-LD conversions.', async(): Promise => { const representation = { metadata } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: 'application/ld+json', weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { 'application/ld+json': 1 }}; await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); }); @@ -44,7 +44,7 @@ describe('A QuadToRdfConverter', (): void => { ) ]), metadata, } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: 'text/turtle', weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }}; const result = await converter.handle({ identifier, representation, preferences }); expect(result).toMatchObject({ binary: true, @@ -67,7 +67,7 @@ describe('A QuadToRdfConverter', (): void => { ) ]), metadata, } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: 'application/ld+json', weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { 'application/ld+json': 1 }}; const result = await converter.handle({ identifier, representation, preferences }); expect(result).toMatchObject({ binary: true, diff --git a/test/unit/storage/conversion/RdfToQuadConverter.test.ts b/test/unit/storage/conversion/RdfToQuadConverter.test.ts index 4bed5ddc2..49a35b844 100644 --- a/test/unit/storage/conversion/RdfToQuadConverter.test.ts +++ b/test/unit/storage/conversion/RdfToQuadConverter.test.ts @@ -28,14 +28,14 @@ describe('A RdfToQuadConverter.test.ts', (): void => { it('can handle turtle to quad conversions.', async(): Promise => { const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'text/turtle' }); const representation = { metadata } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: INTERNAL_QUADS, weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }}; await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); }); it('can handle JSON-LD to quad conversions.', async(): Promise => { const metadata = new RepresentationMetadata({ [CONTENT_TYPE]: 'application/ld+json' }); const representation = { metadata } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: INTERNAL_QUADS, weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }}; await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); }); @@ -45,7 +45,7 @@ describe('A RdfToQuadConverter.test.ts', (): void => { data: streamifyArray([ ' .' ]), metadata, } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: INTERNAL_QUADS, weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }}; const result = await converter.handle({ identifier, representation, preferences }); expect(result).toEqual({ binary: false, @@ -66,7 +66,7 @@ describe('A RdfToQuadConverter.test.ts', (): void => { data: streamifyArray([ '{"@id": "http://test.com/s", "http://test.com/p": { "@id": "http://test.com/o" }}' ]), metadata, } as Representation; - const preferences: RepresentationPreferences = { type: [{ value: INTERNAL_QUADS, weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }}; const result = await converter.handle({ identifier, representation, preferences }); expect(result).toEqual({ binary: false, @@ -87,7 +87,7 @@ describe('A RdfToQuadConverter.test.ts', (): void => { data: streamifyArray([ ' { const basicChecks = async(quads: Quad[]): Promise => { expect(source.getRepresentation).toHaveBeenCalledTimes(1); expect(source.getRepresentation).toHaveBeenLastCalledWith( - { path: 'path' }, { type: [{ value: INTERNAL_QUADS, weight: 1 }]}, + { path: 'path' }, { type: { [INTERNAL_QUADS]: 1 }}, ); expect(source.setRepresentation).toHaveBeenCalledTimes(1); expect(order).toEqual([ 'acquire', 'getRepresentation', 'setRepresentation', 'release' ]); diff --git a/test/unit/storage/routing/PreferenceSupport.test.ts b/test/unit/storage/routing/PreferenceSupport.test.ts index 95b0d37cd..e4e38147b 100644 --- a/test/unit/storage/routing/PreferenceSupport.test.ts +++ b/test/unit/storage/routing/PreferenceSupport.test.ts @@ -7,7 +7,7 @@ import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpE describe('A PreferenceSupport', (): void => { const type = 'internal/quads'; - const preferences: RepresentationPreferences = { type: [{ value: type, weight: 1 }]}; + const preferences: RepresentationPreferences = { type: { [type]: 1 }}; let converter: RepresentationConverter; let support: PreferenceSupport; const identifier: ResourceIdentifier = 'identifier' as any;