feat: Use baseIRI in QuadToRdfConverter.

Closes https://github.com/solid/community-server/issues/512
This commit is contained in:
Ruben Verborgh 2021-01-14 23:42:18 +01:00 committed by Joachim Van Herwegen
parent cabdf7960f
commit 4638ba4bce
5 changed files with 33 additions and 17 deletions

6
package-lock.json generated
View File

@ -7136,9 +7136,9 @@
"dev": true "dev": true
}, },
"n3": { "n3": {
"version": "1.7.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/n3/-/n3-1.7.0.tgz", "resolved": "https://registry.npmjs.org/n3/-/n3-1.8.0.tgz",
"integrity": "sha512-8R0Qj545WnVLQxOfxxyFKzOpO13hF3jhSMJfO0FNqvbsPZDiR9ZDmGGjXAlcoZDf/88OsCYd7rHML284vm1h6A==", "integrity": "sha512-/PEmoB3UJrG6aXGZenDHFBJtmPp2rtfB2YLzAm2dU9stInD+ztvy4fKv5fv2ggsrSlpu7BYDTsz/c6S391uuEg==",
"requires": { "requires": {
"queue-microtask": "^1.1.2", "queue-microtask": "^1.1.2",
"readable-stream": "^3.6.0" "readable-stream": "^3.6.0"

View File

@ -96,7 +96,7 @@
"fetch-sparql-endpoint": "^1.8.0", "fetch-sparql-endpoint": "^1.8.0",
"handlebars": "^4.7.6", "handlebars": "^4.7.6",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"n3": "^1.7.0", "n3": "^1.8.0",
"rdf-parse": "^1.5.0", "rdf-parse": "^1.5.0",
"rdf-serialize": "^1.0.0", "rdf-serialize": "^1.0.0",
"rdf-terms": "^1.5.1", "rdf-terms": "^1.5.1",

View File

@ -24,7 +24,8 @@ export class QuadToRdfConverter extends TypedRepresentationConverter {
); );
} }
public async handle({ representation: quads, preferences }: RepresentationConverterArgs): Promise<Representation> { public async handle({ identifier, representation: quads, preferences }: RepresentationConverterArgs):
Promise<Representation> {
const contentType = matchingMediaTypes(preferences.type, await this.getOutputTypes())[0]; const contentType = matchingMediaTypes(preferences.type, await this.getOutputTypes())[0];
let data: Readable; let data: Readable;
@ -32,7 +33,8 @@ export class QuadToRdfConverter extends TypedRepresentationConverter {
if (/(?:turtle|trig)$/u.test(contentType)) { if (/(?:turtle|trig)$/u.test(contentType)) {
const prefixes = Object.fromEntries(quads.metadata.quads(null, PREFERRED_PREFIX_TERM, null) const prefixes = Object.fromEntries(quads.metadata.quads(null, PREFERRED_PREFIX_TERM, null)
.map(({ subject, object }): [string, string] => [ object.value, subject.value ])); .map(({ subject, object }): [string, string] => [ object.value, subject.value ]));
data = pipeSafely(quads.data, new StreamWriter({ format: contentType, prefixes })); const options = { format: contentType, baseIRI: identifier.path, prefixes };
data = pipeSafely(quads.data, new StreamWriter(options));
// Otherwise, write without prefixes // Otherwise, write without prefixes
} else { } else {
data = rdfSerializer.serialize(quads.data, { contentType }) as Readable; data = rdfSerializer.serialize(quads.data, { contentType }) as Readable;

View File

@ -64,7 +64,7 @@ describe.each(stores)('An LDP handler without auth using %s', (name, { storeUrn,
expect(response.getHeaders()).toHaveProperty('content-type', 'text/turtle'); expect(response.getHeaders()).toHaveProperty('content-type', 'text/turtle');
const data = response._getData().toString(); const data = response._getData().toString();
expect(data).toContain(`<${BASE}/> a ldp:Container`); expect(data).toContain(`<> a ldp:Container`);
expect(response.getHeaders().link).toContain(`<${LDP.Container}>; rel="type"`); expect(response.getHeaders().link).toContain(`<${LDP.Container}>; rel="type"`);
}); });
@ -266,7 +266,7 @@ describe.each(stores)('An LDP handler without auth using %s', (name, { storeUrn,
response = await resourceHelper.performRequest(new URL(`${BASE}/${slug}/`), 'GET', { accept: 'text/turtle' }); response = await resourceHelper.performRequest(new URL(`${BASE}/${slug}/`), 'GET', { accept: 'text/turtle' });
expect(response.statusCode).toBe(200); expect(response.statusCode).toBe(200);
const parser = new Parser(); const parser = new Parser({ baseIRI: `${BASE}/${slug}/` });
const quads = parser.parse(response._getData()); const quads = parser.parse(response._getData());
expect(quads.some((entry): boolean => entry.equals(quad( expect(quads.some((entry): boolean => entry.equals(quad(
namedNode(`${BASE}/${slug}/`), namedNode(`${BASE}/${slug}/`),

View File

@ -12,11 +12,11 @@ import { DC, PREFERRED_PREFIX_TERM } from '../../../../src/util/Vocabularies';
describe('A QuadToRdfConverter', (): void => { describe('A QuadToRdfConverter', (): void => {
const converter = new QuadToRdfConverter(); const converter = new QuadToRdfConverter();
const identifier: ResourceIdentifier = { path: 'path' }; const identifier: ResourceIdentifier = { path: 'http://example.org/foo/bar/' };
let metadata: RepresentationMetadata; let metadata: RepresentationMetadata;
beforeEach((): void => { beforeEach((): void => {
metadata = new RepresentationMetadata(INTERNAL_QUADS); metadata = new RepresentationMetadata(identifier, INTERNAL_QUADS);
}); });
it('supports parsing quads.', async(): Promise<void> => { it('supports parsing quads.', async(): Promise<void> => {
@ -35,7 +35,7 @@ describe('A QuadToRdfConverter', (): void => {
.resolves.toEqual(outputPreferences); .resolves.toEqual(outputPreferences);
}); });
it('can handle quad to turtle conversions.', async(): Promise<void> => { it('can handle quad to Turtle conversions.', async(): Promise<void> => {
const representation = { metadata } as Representation; const representation = { metadata } as Representation;
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }}; const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined();
@ -47,7 +47,7 @@ describe('A QuadToRdfConverter', (): void => {
await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined(); await expect(converter.canHandle({ identifier, representation, preferences })).resolves.toBeUndefined();
}); });
it('converts quads to turtle.', async(): Promise<void> => { it('converts quads to Turtle.', async(): Promise<void> => {
const representation = { const representation = {
data: streamifyArray([ triple( data: streamifyArray([ triple(
namedNode('http://test.com/s'), namedNode('http://test.com/s'),
@ -69,7 +69,7 @@ describe('A QuadToRdfConverter', (): void => {
); );
}); });
it('converts quads with prefixes to turtle.', async(): Promise<void> => { it('converts quads with prefixes to Turtle.', async(): Promise<void> => {
metadata.addQuad(DC.terms.namespace, PREFERRED_PREFIX_TERM, 'dc'); metadata.addQuad(DC.terms.namespace, PREFERRED_PREFIX_TERM, 'dc');
metadata.addQuad('http://test.com/', PREFERRED_PREFIX_TERM, 'test'); metadata.addQuad('http://test.com/', PREFERRED_PREFIX_TERM, 'test');
const representation = { const representation = {
@ -82,10 +82,6 @@ describe('A QuadToRdfConverter', (): void => {
} as Representation; } as Representation;
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }}; const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
const result = await converter.handle({ identifier, representation, preferences }); const result = await converter.handle({ identifier, representation, preferences });
expect(result).toMatchObject({
binary: true,
metadata: expect.any(RepresentationMetadata),
});
expect(result.metadata.contentType).toEqual('text/turtle'); expect(result.metadata.contentType).toEqual('text/turtle');
await expect(stringifyStream(result.data)).resolves.toEqual( await expect(stringifyStream(result.data)).resolves.toEqual(
`@prefix dc: <http://purl.org/dc/terms/>. `@prefix dc: <http://purl.org/dc/terms/>.
@ -96,6 +92,24 @@ test:s dc:modified test:o.
); );
}); });
it('uses the base IRI when converting quads to Turtle.', async(): Promise<void> => {
const representation = {
data: streamifyArray([ triple(
namedNode('http://example.org/foo/bar/'),
namedNode('http://example.org/foo/bar/#abc'),
namedNode('http://example.org/foo/bar/def/ghi'),
) ]),
metadata,
} as Representation;
const preferences: RepresentationPreferences = { type: { 'text/turtle': 1 }};
const result = await converter.handle({ identifier, representation, preferences });
expect(result.metadata.contentType).toEqual('text/turtle');
await expect(stringifyStream(result.data)).resolves.toEqual(
`<> <#abc> <def/ghi>.
`,
);
});
it('converts quads to JSON-LD.', async(): Promise<void> => { it('converts quads to JSON-LD.', async(): Promise<void> => {
metadata.contentType = INTERNAL_QUADS; metadata.contentType = INTERNAL_QUADS;
const representation = { const representation = {