From fdd42bb7b3efda8bfac535ef4ff07f45ea4a524a Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Tue, 26 Oct 2021 16:55:34 +0200 Subject: [PATCH] feat: Add ContentTypeReplacer to conversion chain --- .../representation-conversion/default.json | 2 +- src/storage/conversion/ContentTypeReplacer.ts | 23 +++++++++++-------- .../conversion/ContentTypeReplacer.test.ts | 10 ++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/config/util/representation-conversion/default.json b/config/util/representation-conversion/default.json index 32e972d69..9cd2e64e4 100644 --- a/config/util/representation-conversion/default.json +++ b/config/util/representation-conversion/default.json @@ -21,12 +21,12 @@ "@type": "IfNeededConverter", "comment": "Only continue converting if the requester cannot accept the available content type" }, - { "@id": "urn:solid-server:default:ContentTypeReplacer" }, { "comment": "Automatically finds a path through a set of converters from one type to another.", "@id": "urn:solid-server:default:ChainedConverter", "@type": "ChainedConverter", "converters": [ + { "@id": "urn:solid-server:default:ContentTypeReplacer" }, { "@id": "urn:solid-server:default:RdfToQuadConverter" }, { "@id": "urn:solid-server:default:QuadToRdfConverter" }, { "@id": "urn:solid-server:default:ContainerToTemplateConverter" }, diff --git a/src/storage/conversion/ContentTypeReplacer.ts b/src/storage/conversion/ContentTypeReplacer.ts index 1e82f4b0c..52952f26d 100644 --- a/src/storage/conversion/ContentTypeReplacer.ts +++ b/src/storage/conversion/ContentTypeReplacer.ts @@ -4,7 +4,7 @@ import type { ValuePreferences } from '../../http/representation/RepresentationP import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError'; import { matchesMediaType, getConversionTarget } from './ConversionUtil'; import type { RepresentationConverterArgs } from './RepresentationConverter'; -import { RepresentationConverter } from './RepresentationConverter'; +import { TypedRepresentationConverter } from './TypedRepresentationConverter'; /** * A {@link RepresentationConverter} that changes the content type @@ -13,7 +13,7 @@ import { RepresentationConverter } from './RepresentationConverter'; * Useful for when a content type is binary-compatible with another one; * for instance, all JSON-LD files are valid JSON files. */ -export class ContentTypeReplacer extends RepresentationConverter { +export class ContentTypeReplacer extends TypedRepresentationConverter { private readonly contentTypeMap: Record = {}; /** @@ -40,15 +40,22 @@ export class ContentTypeReplacer extends RepresentationConverter { } } + public async getOutputTypes(contentType: string): Promise { + const supported = Object.keys(this.contentTypeMap) + .filter((type): boolean => matchesMediaType(contentType, type)) + .map((type): ValuePreferences => this.contentTypeMap[type]); + return Object.assign({} as ValuePreferences, ...supported); + } + public async canHandle({ representation, preferences }: RepresentationConverterArgs): Promise { - this.getReplacementType(representation.metadata.contentType, preferences.type); + await this.getReplacementType(representation.metadata.contentType, preferences.type); } /** * Changes the content type on the representation. */ public async handle({ representation, preferences }: RepresentationConverterArgs): Promise { - const contentType = this.getReplacementType(representation.metadata.contentType, preferences.type); + const contentType = await this.getReplacementType(representation.metadata.contentType, preferences.type); const metadata = new RepresentationMetadata(representation.metadata, contentType); return { ...representation, metadata }; } @@ -61,11 +68,9 @@ export class ContentTypeReplacer extends RepresentationConverter { * Find a replacement content type that matches the preferences, * or throws an error if none was found. */ - private getReplacementType(contentType = 'unknown', preferred: ValuePreferences = {}): string { - const supported = Object.keys(this.contentTypeMap) - .filter((type): boolean => matchesMediaType(contentType, type)) - .map((type): ValuePreferences => this.contentTypeMap[type]); - const match = getConversionTarget(Object.assign({} as ValuePreferences, ...supported), preferred); + private async getReplacementType(contentType = 'unknown', preferred: ValuePreferences = {}): Promise { + const supported = await this.getOutputTypes(contentType); + const match = getConversionTarget(supported, preferred); if (!match) { throw new NotImplementedHttpError(`Cannot convert from ${contentType} to ${Object.keys(preferred)}`); } diff --git a/test/unit/storage/conversion/ContentTypeReplacer.test.ts b/test/unit/storage/conversion/ContentTypeReplacer.test.ts index 782d5b53b..77768d332 100644 --- a/test/unit/storage/conversion/ContentTypeReplacer.test.ts +++ b/test/unit/storage/conversion/ContentTypeReplacer.test.ts @@ -97,4 +97,14 @@ describe('A ContentTypeReplacer', (): void => { expect(result.data).toBe(data); expect(result.metadata.contentType).toBe('application/trig'); }); + + it('returns all matching output types.', async(): Promise => { + await expect(converter.getOutputTypes('application/n-triples')).resolves.toEqual({ + 'text/turtle': 1, + 'application/trig': 1, + 'application/n-quads': 1, + 'application/octet-stream': 1, + 'internal/anything': 1, + }); + }); });