mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: store turtle prefixes in metadata when parsing
build: correct package-lock file
This commit is contained in:
parent
9a12152253
commit
66e82dd772
4198
package-lock.json
generated
4198
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -134,7 +134,7 @@
|
|||||||
"pump": "^3.0.0",
|
"pump": "^3.0.0",
|
||||||
"punycode": "^2.1.1",
|
"punycode": "^2.1.1",
|
||||||
"rdf-dereference": "^2.0.0",
|
"rdf-dereference": "^2.0.0",
|
||||||
"rdf-parse": "^2.0.0",
|
"rdf-parse": "^2.1.0",
|
||||||
"rdf-serialize": "^2.0.0",
|
"rdf-serialize": "^2.0.0",
|
||||||
"rdf-terms": "^1.7.1",
|
"rdf-terms": "^1.7.1",
|
||||||
"sparqlalgebrajs": "^4.0.2",
|
"sparqlalgebrajs": "^4.0.2",
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { PassThrough } from 'stream';
|
import { PassThrough } from 'stream';
|
||||||
|
import type { NamedNode } from '@rdfjs/types';
|
||||||
import rdfParser from 'rdf-parse';
|
import rdfParser from 'rdf-parse';
|
||||||
import { BasicRepresentation } from '../../http/representation/BasicRepresentation';
|
import { BasicRepresentation } from '../../http/representation/BasicRepresentation';
|
||||||
import type { Representation } from '../../http/representation/Representation';
|
import type { Representation } from '../../http/representation/Representation';
|
||||||
|
import { RepresentationMetadata } from '../../http/representation/RepresentationMetadata';
|
||||||
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
import { INTERNAL_QUADS } from '../../util/ContentTypes';
|
||||||
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
|
||||||
import { pipeSafely } from '../../util/StreamUtil';
|
import { pipeSafely } from '../../util/StreamUtil';
|
||||||
|
import { PREFERRED_PREFIX_TERM, SOLID_META } from '../../util/Vocabularies';
|
||||||
import { BaseTypedRepresentationConverter } from './BaseTypedRepresentationConverter';
|
import { BaseTypedRepresentationConverter } from './BaseTypedRepresentationConverter';
|
||||||
import type { RepresentationConverterArgs } from './RepresentationConverter';
|
import type { RepresentationConverterArgs } from './RepresentationConverter';
|
||||||
|
|
||||||
@ -20,13 +23,21 @@ export class RdfToQuadConverter extends BaseTypedRepresentationConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async handle({ representation, identifier }: RepresentationConverterArgs): Promise<Representation> {
|
public async handle({ representation, identifier }: RepresentationConverterArgs): Promise<Representation> {
|
||||||
|
const newMetadata = new RepresentationMetadata(representation.metadata, INTERNAL_QUADS);
|
||||||
const rawQuads = rdfParser.parse(representation.data, {
|
const rawQuads = rdfParser.parse(representation.data, {
|
||||||
contentType: representation.metadata.contentType!,
|
contentType: representation.metadata.contentType!,
|
||||||
baseIRI: identifier.path,
|
baseIRI: identifier.path,
|
||||||
|
})
|
||||||
|
// This works only for those cases where the data stream has been completely read before accessing the metadata.
|
||||||
|
// Eg. the PATCH operation, which is the main case why we store the prefixes in metadata here if there are any.
|
||||||
|
// See also https://github.com/CommunitySolidServer/CommunitySolidServer/issues/126
|
||||||
|
.on('prefix', (prefix, iri: NamedNode): void => {
|
||||||
|
newMetadata.addQuad(iri.value, PREFERRED_PREFIX_TERM, prefix, SOLID_META.terms.ResponseMetadata);
|
||||||
});
|
});
|
||||||
|
|
||||||
const pass = new PassThrough({ objectMode: true });
|
const pass = new PassThrough({ objectMode: true });
|
||||||
const data = pipeSafely(rawQuads, pass, (error): Error => new BadRequestHttpError(error.message));
|
const data = pipeSafely(rawQuads, pass, (error): Error => new BadRequestHttpError(error.message));
|
||||||
|
|
||||||
return new BasicRepresentation(data, representation.metadata, INTERNAL_QUADS);
|
return new BasicRepresentation(data, newMetadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { createReadStream } from 'fs';
|
import { createReadStream } from 'fs';
|
||||||
import fetch from 'cross-fetch';
|
import fetch from 'cross-fetch';
|
||||||
|
import type { Quad } from 'n3';
|
||||||
import { DataFactory, Parser, Store } from 'n3';
|
import { DataFactory, Parser, Store } from 'n3';
|
||||||
import { joinFilePath, PIM, RDF } from '../../src/';
|
import { joinFilePath, PIM, RDF } from '../../src/';
|
||||||
import type { App } from '../../src/';
|
import type { App } from '../../src/';
|
||||||
@ -11,7 +12,8 @@ import {
|
|||||||
getPresetConfigPath,
|
getPresetConfigPath,
|
||||||
getTestConfigPath,
|
getTestConfigPath,
|
||||||
getTestFolder,
|
getTestFolder,
|
||||||
instantiateFromConfig, removeFolder,
|
instantiateFromConfig,
|
||||||
|
removeFolder,
|
||||||
} from './Config';
|
} from './Config';
|
||||||
const { literal, namedNode, quad } = DataFactory;
|
const { literal, namedNode, quad } = DataFactory;
|
||||||
|
|
||||||
@ -396,4 +398,70 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
|
|||||||
const response = await fetch(baseUrl, { method: 'PATCH', headers: { 'content-type': 'text/plain' }, body: 'abc' });
|
const response = await fetch(baseUrl, { method: 'PATCH', headers: { 'content-type': 'text/plain' }, body: 'abc' });
|
||||||
expect(response.status).toBe(415);
|
expect(response.status).toBe(415);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('maintains prefixes after PATCH operations.', async(): Promise<void> => {
|
||||||
|
// POST
|
||||||
|
const body = [ '@prefix test: <http://test.com/>.',
|
||||||
|
'test:s1 test:p1 test:o1.',
|
||||||
|
'test:s2 test:p2 test:o2.' ].join('\n');
|
||||||
|
let response = await postResource(baseUrl, { contentType: 'text/turtle', body });
|
||||||
|
const documentUrl = response.headers.get('location')!;
|
||||||
|
|
||||||
|
// PATCH
|
||||||
|
const query = [ 'PREFIX test: <http://test.com/>',
|
||||||
|
'DELETE { test:s1 test:p1 test:o1 }',
|
||||||
|
'INSERT { test:s3 test:p3 test:o3. test:s4 test:p4 test:o4 }',
|
||||||
|
'WHERE {}',
|
||||||
|
].join('\n');
|
||||||
|
await patchResource(documentUrl, query, true);
|
||||||
|
|
||||||
|
// GET
|
||||||
|
response = await getResource(documentUrl);
|
||||||
|
const parser = new Parser();
|
||||||
|
const quads: Quad[] = [];
|
||||||
|
let prefixes: any = {};
|
||||||
|
const text = await response.clone().text();
|
||||||
|
const promise = new Promise<void>((resolve, reject): void => {
|
||||||
|
parser.parse(text, (error, aQuad, prefixHash): any => {
|
||||||
|
if (aQuad) {
|
||||||
|
quads.push(aQuad);
|
||||||
|
}
|
||||||
|
if (!aQuad) {
|
||||||
|
prefixes = prefixHash;
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
quad(
|
||||||
|
namedNode('http://test.com/s2'),
|
||||||
|
namedNode('http://test.com/p2'),
|
||||||
|
namedNode('http://test.com/o2'),
|
||||||
|
),
|
||||||
|
quad(
|
||||||
|
namedNode('http://test.com/s3'),
|
||||||
|
namedNode('http://test.com/p3'),
|
||||||
|
namedNode('http://test.com/o3'),
|
||||||
|
),
|
||||||
|
quad(
|
||||||
|
namedNode('http://test.com/s4'),
|
||||||
|
namedNode('http://test.com/p4'),
|
||||||
|
namedNode('http://test.com/o4'),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
await expectQuads(response, expected, true);
|
||||||
|
expect(prefixes).toEqual({
|
||||||
|
test: 'http://test.com/',
|
||||||
|
});
|
||||||
|
expect(quads).toHaveLength(3);
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
expect(await deleteResource(documentUrl)).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@ import { Readable } from 'stream';
|
|||||||
import arrayifyStream from 'arrayify-stream';
|
import arrayifyStream from 'arrayify-stream';
|
||||||
import { DataFactory } from 'n3';
|
import { DataFactory } from 'n3';
|
||||||
import rdfParser from 'rdf-parse';
|
import rdfParser from 'rdf-parse';
|
||||||
|
import { PREFERRED_PREFIX_TERM, SOLID_META } from '../../../../src';
|
||||||
import { BasicRepresentation } from '../../../../src/http/representation/BasicRepresentation';
|
import { BasicRepresentation } from '../../../../src/http/representation/BasicRepresentation';
|
||||||
import type { Representation } from '../../../../src/http/representation/Representation';
|
import type { Representation } from '../../../../src/http/representation/Representation';
|
||||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||||
@ -11,7 +12,7 @@ import type { ResourceIdentifier } from '../../../../src/http/representation/Res
|
|||||||
import { RdfToQuadConverter } from '../../../../src/storage/conversion/RdfToQuadConverter';
|
import { RdfToQuadConverter } from '../../../../src/storage/conversion/RdfToQuadConverter';
|
||||||
import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes';
|
import { INTERNAL_QUADS } from '../../../../src/util/ContentTypes';
|
||||||
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
||||||
const { namedNode, triple } = DataFactory;
|
const { namedNode, triple, literal, quad } = DataFactory;
|
||||||
|
|
||||||
describe('A RdfToQuadConverter', (): void => {
|
describe('A RdfToQuadConverter', (): void => {
|
||||||
const converter = new RdfToQuadConverter();
|
const converter = new RdfToQuadConverter();
|
||||||
@ -63,6 +64,30 @@ describe('A RdfToQuadConverter', (): void => {
|
|||||||
) ]);
|
) ]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('emits on prefixes when converting turtle to quads.', async(): Promise<void> => {
|
||||||
|
const id: ResourceIdentifier = { path: 'http://example.com/' };
|
||||||
|
const metadata = new RepresentationMetadata('text/turtle');
|
||||||
|
const representation = new BasicRepresentation(`
|
||||||
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||||
|
|
||||||
|
<http://test.com/s> a foaf:Person.
|
||||||
|
`
|
||||||
|
, metadata);
|
||||||
|
const preferences: RepresentationPreferences = { type: { [INTERNAL_QUADS]: 1 }};
|
||||||
|
const result = await converter.handle({ identifier: id, representation, preferences });
|
||||||
|
expect(result).toEqual({
|
||||||
|
binary: false,
|
||||||
|
data: expect.any(Readable),
|
||||||
|
metadata: expect.any(RepresentationMetadata),
|
||||||
|
});
|
||||||
|
expect(result.metadata.contentType).toEqual(INTERNAL_QUADS);
|
||||||
|
await arrayifyStream(result.data);
|
||||||
|
|
||||||
|
expect(result.metadata.quads(null, PREFERRED_PREFIX_TERM, null)).toBeRdfIsomorphic([
|
||||||
|
quad(namedNode('http://xmlns.com/foaf/0.1/'), PREFERRED_PREFIX_TERM, literal('foaf'), SOLID_META.terms.ResponseMetadata),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('converts JSON-LD to quads.', async(): Promise<void> => {
|
it('converts JSON-LD to quads.', async(): Promise<void> => {
|
||||||
const metadata = new RepresentationMetadata('application/ld+json');
|
const metadata = new RepresentationMetadata('application/ld+json');
|
||||||
const representation = new BasicRepresentation(
|
const representation = new BasicRepresentation(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user