mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Dynamically determine matching types in ChainedConverter
This commit is contained in:
parent
734f7e7f0f
commit
af4a82f4c1
@ -1,53 +1,104 @@
|
|||||||
import { Representation } from '../../ldp/representation/Representation';
|
import { Representation } from '../../ldp/representation/Representation';
|
||||||
import { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
|
import { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
|
||||||
import { RepresentationConverter, RepresentationConverterArgs } from './RepresentationConverter';
|
import { matchingMediaType } from '../../util/Util';
|
||||||
|
import { RepresentationConverterArgs } from './RepresentationConverter';
|
||||||
|
import { TypedRepresentationConverter } from './TypedRepresentationConverter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A meta converter that takes an array of other converters as input.
|
* A meta converter that takes an array of other converters as input.
|
||||||
* It chains these converters based on given intermediate types that are supported by converters on either side.
|
* It chains these converters by finding intermediate types that are supported by converters on either side.
|
||||||
*/
|
*/
|
||||||
export class ChainedConverter extends RepresentationConverter {
|
export class ChainedConverter extends TypedRepresentationConverter {
|
||||||
private readonly converters: RepresentationConverter[];
|
private readonly converters: TypedRepresentationConverter[];
|
||||||
private readonly chainTypes: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the chain of converters based on the input.
|
* Creates the chain of converters based on the input.
|
||||||
* The list of `converters` needs to be at least 2 long,
|
* The list of `converters` needs to be at least 2 long.
|
||||||
* and `chainTypes` needs to be the same length - 1,
|
|
||||||
* as each type at index `i` corresponds to the output type of converter `i`
|
|
||||||
* and input type of converter `i+1`.
|
|
||||||
* @param converters - The chain of converters.
|
* @param converters - The chain of converters.
|
||||||
* @param chainTypes - The intermediate types of the chain.
|
|
||||||
*/
|
*/
|
||||||
public constructor(converters: RepresentationConverter[], chainTypes: string[]) {
|
public constructor(converters: TypedRepresentationConverter[]) {
|
||||||
super();
|
super();
|
||||||
if (converters.length < 2) {
|
if (converters.length < 2) {
|
||||||
throw new Error('At least 2 converters are required.');
|
throw new Error('At least 2 converters are required.');
|
||||||
}
|
}
|
||||||
if (chainTypes.length !== converters.length - 1) {
|
this.converters = [ ...converters ];
|
||||||
throw new Error('1 type is required per converter chain.');
|
|
||||||
}
|
}
|
||||||
this.converters = converters;
|
|
||||||
this.chainTypes = chainTypes;
|
protected get first(): TypedRepresentationConverter {
|
||||||
|
return this.converters[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get last(): TypedRepresentationConverter {
|
||||||
|
return this.converters[this.converters.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getInputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return this.first.getInputTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOutputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return this.last.getOutputTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
||||||
|
// We assume a chain can be constructed, otherwise there would be a configuration issue
|
||||||
// Check if the first converter can handle the input
|
// Check if the first converter can handle the input
|
||||||
const preferences: RepresentationPreferences = { type: [{ value: this.chainTypes[0], weight: 1 }]};
|
const firstChain = await this.getMatchingType(this.converters[0], this.converters[1]);
|
||||||
await this.converters[0].canHandle({ ...input, preferences });
|
const preferences: RepresentationPreferences = { type: [{ value: firstChain, weight: 1 }]};
|
||||||
|
await this.first.canHandle({ ...input, preferences });
|
||||||
|
|
||||||
// Check if the last converter can produce the output
|
// Check if the last converter can produce the output
|
||||||
|
const idx = this.converters.length - 1;
|
||||||
|
const lastChain = await this.getMatchingType(this.converters[idx - 1], this.converters[idx]);
|
||||||
const representation: Representation = { ...input.representation };
|
const representation: Representation = { ...input.representation };
|
||||||
representation.metadata = { ...input.representation.metadata, contentType: this.chainTypes.slice(-1)[0] };
|
representation.metadata = { ...input.representation.metadata, contentType: lastChain };
|
||||||
await this.converters.slice(-1)[0].canHandle({ ...input, representation });
|
await this.last.canHandle({ ...input, representation });
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(input: RepresentationConverterArgs): Promise<Representation> {
|
public async handle(input: RepresentationConverterArgs): Promise<Representation> {
|
||||||
const args = { ...input };
|
const args = { ...input };
|
||||||
for (let i = 0; i < this.chainTypes.length; ++i) {
|
for (let i = 0; i < this.converters.length - 1; ++i) {
|
||||||
args.preferences = { type: [{ value: this.chainTypes[i], weight: 1 }]};
|
const value = await this.getMatchingType(this.converters[i], this.converters[i + 1]);
|
||||||
|
args.preferences = { type: [{ value, weight: 1 }]};
|
||||||
args.representation = await this.converters[i].handle(args);
|
args.representation = await this.converters[i].handle(args);
|
||||||
}
|
}
|
||||||
return this.converters.slice(-1)[0].handle(args);
|
args.preferences = input.preferences;
|
||||||
|
return this.last.handle(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the best media type that can be used to chain 2 converters.
|
||||||
|
*/
|
||||||
|
protected async getMatchingType(left: TypedRepresentationConverter, right: TypedRepresentationConverter):
|
||||||
|
Promise<string> {
|
||||||
|
const leftTypes = await left.getOutputTypes();
|
||||||
|
const rightTypes = await right.getInputTypes();
|
||||||
|
let bestMatch: { type: string; weight: number } = { type: 'invalid', weight: 0 };
|
||||||
|
|
||||||
|
// Try to find the matching type with the best weight
|
||||||
|
const leftKeys = Object.keys(leftTypes);
|
||||||
|
const rightKeys = Object.keys(rightTypes);
|
||||||
|
for (const leftType of leftKeys) {
|
||||||
|
const leftWeight = leftTypes[leftType];
|
||||||
|
if (leftWeight <= bestMatch.weight) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const rightType of rightKeys) {
|
||||||
|
const rightWeight = rightTypes[rightType];
|
||||||
|
const weight = leftWeight * rightWeight;
|
||||||
|
if (weight > bestMatch.weight && matchingMediaType(leftType, rightType)) {
|
||||||
|
bestMatch = { type: leftType, weight };
|
||||||
|
if (weight === 1) {
|
||||||
|
return bestMatch.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestMatch.weight === 0) {
|
||||||
|
throw new Error(`No match found between ${leftKeys} and ${rightKeys}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMatch.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,21 @@ import { RepresentationMetadata } from '../../ldp/representation/RepresentationM
|
|||||||
import { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
|
import { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
|
||||||
import { CONTENT_TYPE_QUADS, DATA_TYPE_BINARY } from '../../util/ContentTypes';
|
import { CONTENT_TYPE_QUADS, DATA_TYPE_BINARY } from '../../util/ContentTypes';
|
||||||
import { checkRequest, matchingTypes } from './ConversionUtil';
|
import { checkRequest, matchingTypes } from './ConversionUtil';
|
||||||
import { RepresentationConverter, RepresentationConverterArgs } from './RepresentationConverter';
|
import { RepresentationConverterArgs } from './RepresentationConverter';
|
||||||
|
import { TypedRepresentationConverter } from './TypedRepresentationConverter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts `internal/quads` to most major RDF serializations.
|
* Converts `internal/quads` to most major RDF serializations.
|
||||||
*/
|
*/
|
||||||
export class QuadToRdfConverter extends RepresentationConverter {
|
export class QuadToRdfConverter extends TypedRepresentationConverter {
|
||||||
|
public async getInputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return { [CONTENT_TYPE_QUADS]: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOutputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return rdfSerializer.getContentTypesPrioritized();
|
||||||
|
}
|
||||||
|
|
||||||
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
||||||
checkRequest(input, [ CONTENT_TYPE_QUADS ], await rdfSerializer.getContentTypes());
|
checkRequest(input, [ CONTENT_TYPE_QUADS ], await rdfSerializer.getContentTypes());
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,21 @@ import { RepresentationMetadata } from '../../ldp/representation/RepresentationM
|
|||||||
import { CONTENT_TYPE_QUADS, DATA_TYPE_QUAD } from '../../util/ContentTypes';
|
import { CONTENT_TYPE_QUADS, DATA_TYPE_QUAD } from '../../util/ContentTypes';
|
||||||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||||
import { checkRequest } from './ConversionUtil';
|
import { checkRequest } from './ConversionUtil';
|
||||||
import { RepresentationConverter, RepresentationConverterArgs } from './RepresentationConverter';
|
import { RepresentationConverterArgs } from './RepresentationConverter';
|
||||||
|
import { TypedRepresentationConverter } from './TypedRepresentationConverter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts most major RDF serializations to `internal/quads`.
|
* Converts most major RDF serializations to `internal/quads`.
|
||||||
*/
|
*/
|
||||||
export class RdfToQuadConverter extends RepresentationConverter {
|
export class RdfToQuadConverter extends TypedRepresentationConverter {
|
||||||
|
public async getInputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return rdfParser.getContentTypesPrioritized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOutputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return { [CONTENT_TYPE_QUADS]: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
||||||
checkRequest(input, await rdfParser.getContentTypes(), [ CONTENT_TYPE_QUADS ]);
|
checkRequest(input, await rdfParser.getContentTypes(), [ CONTENT_TYPE_QUADS ]);
|
||||||
}
|
}
|
||||||
|
20
src/storage/conversion/TypedRepresentationConverter.ts
Normal file
20
src/storage/conversion/TypedRepresentationConverter.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { RepresentationConverter } from './RepresentationConverter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link RepresentationConverter} that allows requesting the supported types.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public abstract getInputTypes(): Promise<{ [contentType: string]: number }>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public abstract getOutputTypes(): Promise<{ [contentType: string]: number }>;
|
||||||
|
}
|
@ -2,34 +2,40 @@ import { Representation } from '../../../../src/ldp/representation/Representatio
|
|||||||
import { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
|
import { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
|
||||||
import { ChainedConverter } from '../../../../src/storage/conversion/ChainedConverter';
|
import { ChainedConverter } from '../../../../src/storage/conversion/ChainedConverter';
|
||||||
import { checkRequest } from '../../../../src/storage/conversion/ConversionUtil';
|
import { checkRequest } from '../../../../src/storage/conversion/ConversionUtil';
|
||||||
import {
|
import { RepresentationConverterArgs } from '../../../../src/storage/conversion/RepresentationConverter';
|
||||||
RepresentationConverter,
|
import { TypedRepresentationConverter } from '../../../../src/storage/conversion/TypedRepresentationConverter';
|
||||||
RepresentationConverterArgs,
|
|
||||||
} from '../../../../src/storage/conversion/RepresentationConverter';
|
|
||||||
|
|
||||||
class DummyConverter extends RepresentationConverter {
|
class DummyConverter extends TypedRepresentationConverter {
|
||||||
private readonly inType: string;
|
private readonly inTypes: { [contentType: string]: number };
|
||||||
private readonly outType: string;
|
private readonly outTypes: { [contentType: string]: number };
|
||||||
|
|
||||||
public constructor(inType: string, outType: string) {
|
public constructor(inTypes: { [contentType: string]: number }, outTypes: { [contentType: string]: number }) {
|
||||||
super();
|
super();
|
||||||
this.inType = inType;
|
this.inTypes = inTypes;
|
||||||
this.outType = outType;
|
this.outTypes = outTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getInputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return this.inTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getOutputTypes(): Promise<{ [contentType: string]: number }> {
|
||||||
|
return this.outTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
public async canHandle(input: RepresentationConverterArgs): Promise<void> {
|
||||||
checkRequest(input, [ this.inType ], [ this.outType ]);
|
checkRequest(input, Object.keys(this.inTypes), Object.keys(this.outTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(input: RepresentationConverterArgs): Promise<Representation> {
|
public async handle(input: RepresentationConverterArgs): Promise<Representation> {
|
||||||
const representation: Representation = { ...input.representation };
|
const representation: Representation = { ...input.representation };
|
||||||
representation.metadata = { ...input.representation.metadata, contentType: this.outType };
|
representation.metadata = { ...input.representation.metadata, contentType: input.preferences.type![0].value };
|
||||||
return representation;
|
return representation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('A ChainedConverter', (): void => {
|
describe('A ChainedConverter', (): void => {
|
||||||
let converters: RepresentationConverter[];
|
let converters: TypedRepresentationConverter[];
|
||||||
let converter: ChainedConverter;
|
let converter: ChainedConverter;
|
||||||
let representation: Representation;
|
let representation: Representation;
|
||||||
let preferences: RepresentationPreferences;
|
let preferences: RepresentationPreferences;
|
||||||
@ -37,26 +43,32 @@ describe('A ChainedConverter', (): void => {
|
|||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
converters = [
|
converters = [
|
||||||
new DummyConverter('text/turtle', 'chain/1'),
|
new DummyConverter({ 'text/turtle': 1 }, { 'chain/1': 0.9, 'chain/x': 0.5 }),
|
||||||
new DummyConverter('chain/1', 'chain/2'),
|
new DummyConverter({ 'chain/*': 1, 'chain/x': 0.5 }, { 'chain/2': 1 }),
|
||||||
new DummyConverter('chain/2', 'internal/quads'),
|
new DummyConverter({ 'chain/2': 1 }, { 'internal/quads': 1 }),
|
||||||
];
|
];
|
||||||
converter = new ChainedConverter(converters, [ 'chain/1', 'chain/2' ]);
|
converter = new ChainedConverter(converters);
|
||||||
|
|
||||||
representation = { metadata: { contentType: 'text/turtle' } as any } as Representation;
|
representation = { metadata: { contentType: 'text/turtle' } as any } as Representation;
|
||||||
preferences = { type: [{ value: 'internal/quads', weight: 1 }]};
|
preferences = { type: [{ value: 'internal/quads', weight: 1 }]};
|
||||||
args = { representation, preferences, identifier: { path: 'path' }};
|
args = { representation, preferences, identifier: { path: 'path' }};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('needs at least 2 converter and n-1 chains.', async(): Promise<void> => {
|
it('needs at least 2 converters.', async(): Promise<void> => {
|
||||||
expect((): any => new ChainedConverter([], [])).toThrow('At least 2 converters are required.');
|
expect((): any => new ChainedConverter([])).toThrow('At least 2 converters are required.');
|
||||||
expect((): any => new ChainedConverter([ converters[0] ], [])).toThrow('At least 2 converters are required.');
|
expect((): any => new ChainedConverter([ converters[0] ])).toThrow('At least 2 converters are required.');
|
||||||
expect((): any => new ChainedConverter([ converters[0], converters[1] ], []))
|
expect(new ChainedConverter([ converters[0], converters[1] ]))
|
||||||
.toThrow('1 type is required per converter chain.');
|
|
||||||
expect(new ChainedConverter([ converters[0], converters[1] ], [ 'apple' ]))
|
|
||||||
.toBeInstanceOf(ChainedConverter);
|
.toBeInstanceOf(ChainedConverter);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports the same inputs as the first converter of the chain.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getInputTypes()).resolves.toEqual(await converters[0].getInputTypes());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports the same outputs as the last converter of the chain.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getOutputTypes()).resolves.toEqual(await converters[2].getOutputTypes());
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle requests with the correct in- and output.', async(): Promise<void> => {
|
it('can handle requests with the correct in- and output.', async(): Promise<void> => {
|
||||||
await expect(converter.canHandle(args)).resolves.toBeUndefined();
|
await expect(converter.canHandle(args)).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
@ -82,4 +94,13 @@ describe('A ChainedConverter', (): void => {
|
|||||||
expect((converters[1] as any).handle).toHaveBeenCalledTimes(1);
|
expect((converters[1] as any).handle).toHaveBeenCalledTimes(1);
|
||||||
expect((converters[2] as any).handle).toHaveBeenCalledTimes(1);
|
expect((converters[2] as any).handle).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('errors if there is no valid chain at runtime.', async(): Promise<void> => {
|
||||||
|
converters = [
|
||||||
|
new DummyConverter({ 'text/turtle': 1 }, { 'chain/1': 0.9, 'chain/x': 0.5 }),
|
||||||
|
new DummyConverter({ 'chain/2': 1 }, { 'internal/quads': 1 }),
|
||||||
|
];
|
||||||
|
converter = new ChainedConverter(converters);
|
||||||
|
await expect(converter.handle(args)).rejects.toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { namedNode, triple } from '@rdfjs/data-model';
|
import { namedNode, triple } from '@rdfjs/data-model';
|
||||||
|
import rdfSerializer from 'rdf-serialize';
|
||||||
import stringifyStream from 'stream-to-string';
|
import stringifyStream from 'stream-to-string';
|
||||||
import streamifyArray from 'streamify-array';
|
import streamifyArray from 'streamify-array';
|
||||||
import { Representation } from '../../../../src/ldp/representation/Representation';
|
import { Representation } from '../../../../src/ldp/representation/Representation';
|
||||||
@ -11,6 +12,14 @@ describe('A QuadToRdfConverter', (): void => {
|
|||||||
const converter = new QuadToRdfConverter();
|
const converter = new QuadToRdfConverter();
|
||||||
const identifier: ResourceIdentifier = { path: 'path' };
|
const identifier: ResourceIdentifier = { path: 'path' };
|
||||||
|
|
||||||
|
it('supports parsing quads.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getInputTypes()).resolves.toEqual({ [CONTENT_TYPE_QUADS]: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports serializing as the same types as rdfSerializer.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getOutputTypes()).resolves.toEqual(await rdfSerializer.getContentTypesPrioritized());
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle quad to turtle conversions.', async(): Promise<void> => {
|
it('can handle quad to turtle conversions.', async(): Promise<void> => {
|
||||||
const representation = { metadata: { contentType: CONTENT_TYPE_QUADS }} as Representation;
|
const representation = { metadata: { contentType: CONTENT_TYPE_QUADS }} as Representation;
|
||||||
const preferences: RepresentationPreferences = { type: [{ value: 'text/turtle', weight: 1 }]};
|
const preferences: RepresentationPreferences = { type: [{ value: 'text/turtle', weight: 1 }]};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { namedNode, triple } from '@rdfjs/data-model';
|
import { namedNode, triple } from '@rdfjs/data-model';
|
||||||
import arrayifyStream from 'arrayify-stream';
|
import arrayifyStream from 'arrayify-stream';
|
||||||
|
import rdfParser from 'rdf-parse';
|
||||||
import streamifyArray from 'streamify-array';
|
import streamifyArray from 'streamify-array';
|
||||||
import { Representation } from '../../../../src/ldp/representation/Representation';
|
import { Representation } from '../../../../src/ldp/representation/Representation';
|
||||||
import { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
|
import { RepresentationPreferences } from '../../../../src/ldp/representation/RepresentationPreferences';
|
||||||
@ -13,6 +14,14 @@ describe('A RdfToQuadConverter.test.ts', (): void => {
|
|||||||
const converter = new RdfToQuadConverter();
|
const converter = new RdfToQuadConverter();
|
||||||
const identifier: ResourceIdentifier = { path: 'path' };
|
const identifier: ResourceIdentifier = { path: 'path' };
|
||||||
|
|
||||||
|
it('supports parsing the same types as rdfParser.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getInputTypes()).resolves.toEqual(await rdfParser.getContentTypesPrioritized());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports serializing as quads.', async(): Promise<void> => {
|
||||||
|
await expect(converter.getOutputTypes()).resolves.toEqual({ [CONTENT_TYPE_QUADS]: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle turtle to quad conversions.', async(): Promise<void> => {
|
it('can handle turtle to quad conversions.', async(): Promise<void> => {
|
||||||
const representation = { metadata: { contentType: 'text/turtle' }} as Representation;
|
const representation = { metadata: { contentType: 'text/turtle' }} as Representation;
|
||||||
const preferences: RepresentationPreferences = { type: [{ value: CONTENT_TYPE_QUADS, weight: 1 }]};
|
const preferences: RepresentationPreferences = { type: [{ value: CONTENT_TYPE_QUADS, weight: 1 }]};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user