From ab06dd30f3f8b0538b693fe50dd3d1f70c035b25 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Fri, 20 Aug 2021 16:46:32 +0200 Subject: [PATCH] feat: Allow filtering in ConstantConverter based on type --- src/storage/conversion/ConstantConverter.ts | 21 ++++++++++++++++- .../conversion/ConstantConverter.test.ts | 23 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/storage/conversion/ConstantConverter.ts b/src/storage/conversion/ConstantConverter.ts index 040d5279a..37c8042ef 100644 --- a/src/storage/conversion/ConstantConverter.ts +++ b/src/storage/conversion/ConstantConverter.ts @@ -23,6 +23,14 @@ export interface ConstantConverterOptions { * The minimum requested quality/preference before this should trigger. */ minQuality?: number; + /** + * Media ranges for which the conversion should happen. + */ + enabledMediaRanges?: string[]; + /** + * Media ranges for which the conversion should not happen. + */ + disabledMediaRanges?: string[]; } /** @@ -57,6 +65,8 @@ export class ConstantConverter extends RepresentationConverter { container: options.container ?? true, document: options.document ?? true, minQuality: options.minQuality ?? 0, + enabledMediaRanges: options.enabledMediaRanges ?? [ '*/*' ], + disabledMediaRanges: options.disabledMediaRanges ?? [], }; } @@ -83,10 +93,19 @@ export class ConstantConverter extends RepresentationConverter { throw new NotImplementedHttpError(`Preference is lower than the specified minimum quality`); } + const sourceContentType = representation.metadata.contentType ?? ''; // Do not replace the representation if it already has our content type - if (matchesMediaType(representation.metadata.contentType ?? '', this.contentType)) { + if (matchesMediaType(sourceContentType, this.contentType)) { throw new NotImplementedHttpError(`Representation is already ${this.contentType}`); } + + // Only replace the representation if it matches the media range settings + if (!this.options.enabledMediaRanges.some((type): boolean => matchesMediaType(sourceContentType, type))) { + throw new NotImplementedHttpError(`${sourceContentType} is not one of the enabled media types.`); + } + if (this.options.disabledMediaRanges.some((type): boolean => matchesMediaType(sourceContentType, type))) { + throw new NotImplementedHttpError(`${sourceContentType} is one of the disabled media types.`); + } } public async handle({ representation }: RepresentationConverterArgs): Promise { diff --git a/test/unit/storage/conversion/ConstantConverter.test.ts b/test/unit/storage/conversion/ConstantConverter.test.ts index 27e81ff78..964dc359d 100644 --- a/test/unit/storage/conversion/ConstantConverter.test.ts +++ b/test/unit/storage/conversion/ConstantConverter.test.ts @@ -3,6 +3,7 @@ import arrayifyStream from 'arrayify-stream'; import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata'; import type { ConstantConverterOptions } from '../../../../src/storage/conversion/ConstantConverter'; import { ConstantConverter } from '../../../../src/storage/conversion/ConstantConverter'; +import { CONTENT_TYPE } from '../../../../src/util/Vocabularies'; const createReadStream = jest.spyOn(fs, 'createReadStream').mockReturnValue('file contents' as any); @@ -12,7 +13,7 @@ describe('A ConstantConverter', (): void => { let converter: ConstantConverter; beforeEach(async(): Promise => { - options = { container: true, document: true, minQuality: 1 }; + options = { container: true, document: true, minQuality: 1, enabledMediaRanges: [ '*/*' ], disabledMediaRanges: []}; converter = new ConstantConverter('abc/def/index.html', 'text/html', options); }); @@ -69,6 +70,26 @@ describe('A ConstantConverter', (): void => { await expect(converter.canHandle(args)).rejects.toThrow('Representation is already text/html'); }); + it('does not support representations if their content-type is not enabled.', async(): Promise => { + const preferences = { type: { 'text/html': 1 }}; + const representation = { metadata: new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }) } as any; + const args = { identifier: { path: 'container/' }, representation, preferences }; + + converter = new ConstantConverter('abc/def/index.html', 'text/html', { enabledMediaRanges: [ 'text/turtle' ]}); + + await expect(converter.canHandle(args)).rejects.toThrow('text/plain is not one of the enabled media types.'); + }); + + it('does not support representations if their content-type is disabled.', async(): Promise => { + const preferences = { type: { 'text/html': 1 }}; + const representation = { metadata: new RepresentationMetadata({ [CONTENT_TYPE]: 'text/plain' }) } as any; + const args = { identifier: { path: 'container/' }, representation, preferences }; + + converter = new ConstantConverter('abc/def/index.html', 'text/html', { disabledMediaRanges: [ 'text/*' ]}); + + await expect(converter.canHandle(args)).rejects.toThrow('text/plain is one of the disabled media types.'); + }); + it('supports representations with an unknown content type.', async(): Promise => { const preferences = { type: { 'text/html': 1 }}; const metadata = new RepresentationMetadata();