Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/storage/conversion/ConstantConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}

/**
Expand Down Expand Up @@ -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 ?? [],
};
}

Expand All @@ -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<Representation> {
Expand Down
23 changes: 22 additions & 1 deletion test/unit/storage/conversion/ConstantConverter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -12,7 +13,7 @@ describe('A ConstantConverter', (): void => {
let converter: ConstantConverter;

beforeEach(async(): Promise<void> => {
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);
});

Expand Down Expand Up @@ -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<void> => {
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<void> => {
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<void> => {
const preferences = { type: { 'text/html': 1 }};
const metadata = new RepresentationMetadata();
Expand Down