feat: Make internal/quads unacceptable output

This commit is contained in:
Joachim Van Herwegen 2020-11-09 11:33:35 +01:00
parent 69ed2e069f
commit 715ba126f9
3 changed files with 35 additions and 6 deletions

View File

@ -1,5 +1,6 @@
import type { RepresentationPreference } from '../../ldp/representation/RepresentationPreference';
import type { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
import { INTERNAL_ALL } from '../../util/ContentTypes';
import { InternalServerError } from '../../util/errors/InternalServerError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { matchingMediaType } from '../../util/Util';
@ -8,6 +9,9 @@ import type { RepresentationConverterArgs } from './RepresentationConverter';
/**
* Filters media types based on the given preferences.
* Based on RFC 7231 - Content negotiation.
* Will add a default `internal/*;q=0` to the preferences to prevent accidental use of internal types.
* Since more specific media ranges override less specific ones,
* this will be ignored if there is a specific internal type preference.
*
* @param preferences - Preferences for output type.
* @param types - Media types to compare to the preferences.
@ -31,6 +35,11 @@ RepresentationPreference[] => {
return map;
}, {});
// Prevent accidental use of internal types
if (!prefMap[INTERNAL_ALL]) {
prefMap[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

View File

@ -4,4 +4,5 @@ export const APPLICATION_OCTET_STREAM = 'application/octet-stream';
export const APPLICATION_SPARQL_UPDATE = 'application/sparql-update';
// Internal (non-exposed) content types
export const INTERNAL_ALL = 'internal/*';
export const INTERNAL_QUADS = 'internal/quads';

View File

@ -19,22 +19,22 @@ describe('A ConversionUtil', (): void => {
describe('#checkRequest', (): void => {
it('requires an input type.', async(): Promise<void> => {
const preferences: RepresentationPreferences = {};
expect((): any => checkRequest({ identifier, representation, preferences }, [ '*/*' ], [ '*/*' ]))
expect((): any => checkRequest({ identifier, representation, preferences }, [ 'a/x' ], [ 'a/x' ]))
.toThrow('Input type required for conversion.');
});
it('requires a matching input type.', async(): Promise<void> => {
metadata.contentType = 'a/x';
const preferences: RepresentationPreferences = { type: [{ value: 'b/x', weight: 1 }]};
expect((): any => checkRequest({ identifier, representation, preferences }, [ 'c/x' ], [ '*/*' ]))
.toThrow('Can only convert from c/x to */*.');
expect((): any => checkRequest({ 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<void> => {
metadata.contentType = 'a/x';
const preferences: RepresentationPreferences = { type: [{ value: 'b/x', weight: 1 }]};
expect((): any => checkRequest({ identifier, representation, preferences }, [ '*/*' ], [ 'c/x' ]))
.toThrow('Can only convert from */* to c/x.');
expect((): any => checkRequest({ 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<void> => {
@ -48,7 +48,7 @@ describe('A ConversionUtil', (): void => {
describe('#matchingTypes', (): void => {
it('requires type preferences.', async(): Promise<void> => {
const preferences: RepresentationPreferences = {};
expect((): any => matchingTypes(preferences, [ '*/*' ]))
expect((): any => matchingTypes(preferences, [ 'a/b' ]))
.toThrow('Output type required for conversion.');
});
@ -71,5 +71,24 @@ describe('A ConversionUtil', (): void => {
expect((): any => matchingTypes(preferences, [ 'noType' ]))
.toThrow(new InternalServerError(`Unexpected type preference: noType`));
});
it('filters out internal types.', async(): Promise<void> => {
const preferences: RepresentationPreferences = { type: [{ value: '*/*', weight: 1 }]};
expect(matchingTypes(preferences, [ 'a/x', 'internal/quads' ])).toEqual([{ value: 'a/x', weight: 1 }]);
});
it('keeps internal types that are specifically requested.', async(): Promise<void> => {
const preferences: RepresentationPreferences =
{ type: [{ value: '*/*', weight: 1 }, { value: 'internal/*', weight: 0.5 }]};
expect(matchingTypes(preferences, [ 'a/x', 'internal/quads' ]))
.toEqual([{ value: 'a/x', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]);
});
it('takes the most relevant weight for a type.', async(): Promise<void> => {
const preferences: RepresentationPreferences =
{ type: [{ value: '*/*', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]};
expect(matchingTypes(preferences, [ 'a/x', 'internal/quads' ]))
.toEqual([{ value: 'a/x', weight: 1 }, { value: 'internal/quads', weight: 0.5 }]);
});
});
});