mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
fix: Remove metadata content-type assumption from QuadUtil
This commit is contained in:
parent
1464288b0f
commit
a114d00827
@ -246,7 +246,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
if (representation.metadata.contentType === INTERNAL_QUADS) {
|
||||
quads = await arrayifyStream(representation.data);
|
||||
} else {
|
||||
quads = await parseQuads(representation.data);
|
||||
quads = await parseQuads(representation.data, representation.metadata.contentType);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
|
@ -94,7 +94,7 @@ export class FileDataAccessor implements DataAccessor {
|
||||
} catch (error: unknown) {
|
||||
// Delete the metadata if there was an error writing the file
|
||||
if (wroteMetadata) {
|
||||
await fsPromises.unlink(await this.getMetadataPath(link.identifier));
|
||||
await fsPromises.unlink((await this.getMetadataLink(link.identifier)).filePath);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@ -125,7 +125,7 @@ export class FileDataAccessor implements DataAccessor {
|
||||
const stats = await this.getStats(link.filePath);
|
||||
|
||||
try {
|
||||
await fsPromises.unlink(await this.getMetadataPath(link.identifier));
|
||||
await fsPromises.unlink((await this.getMetadataLink(link.identifier)).filePath);
|
||||
} catch (error: unknown) {
|
||||
// Ignore if it doesn't exist
|
||||
if (!isSystemError(error) || error.code !== 'ENOENT') {
|
||||
@ -161,11 +161,11 @@ export class FileDataAccessor implements DataAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates file path that corresponds to the metadata file of the given identifier.
|
||||
* Starts from the identifier to make sure any potentially added extension has no impact on the path.
|
||||
* Generates ResourceLink that corresponds to the metadata resource of the given identifier.
|
||||
*/
|
||||
private async getMetadataPath(identifier: ResourceIdentifier): Promise<string> {
|
||||
return (await this.resourceMapper.mapUrlToFilePath({ path: `${identifier.path}.meta` })).filePath;
|
||||
private async getMetadataLink(identifier: ResourceIdentifier): Promise<ResourceLink> {
|
||||
const metaIdentifier = { path: `${identifier.path}.meta` };
|
||||
return this.resourceMapper.mapUrlToFilePath(metaIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,19 +213,20 @@ export class FileDataAccessor implements DataAccessor {
|
||||
metadata.removeAll(RDF.type);
|
||||
metadata.removeAll(CONTENT_TYPE);
|
||||
const quads = metadata.quads();
|
||||
const metadataPath = await this.getMetadataPath(link.identifier);
|
||||
const metadataLink = await this.getMetadataLink(link.identifier);
|
||||
let wroteMetadata: boolean;
|
||||
|
||||
// Write metadata to file if there are quads remaining
|
||||
if (quads.length > 0) {
|
||||
const serializedMetadata = serializeQuads(quads);
|
||||
await this.writeDataFile(metadataPath, serializedMetadata);
|
||||
// Determine required content-type based on mapper
|
||||
const serializedMetadata = serializeQuads(quads, metadataLink.contentType);
|
||||
await this.writeDataFile(metadataLink.filePath, serializedMetadata);
|
||||
wroteMetadata = true;
|
||||
|
||||
// Delete (potentially) existing metadata file if no metadata needs to be stored
|
||||
} else {
|
||||
try {
|
||||
await fsPromises.unlink(metadataPath);
|
||||
await fsPromises.unlink(metadataLink.filePath);
|
||||
} catch (error: unknown) {
|
||||
// Metadata file doesn't exist so nothing needs to be removed
|
||||
if (!isSystemError(error) || error.code !== 'ENOENT') {
|
||||
@ -260,13 +261,13 @@ export class FileDataAccessor implements DataAccessor {
|
||||
*/
|
||||
private async getRawMetadata(identifier: ResourceIdentifier): Promise<Quad[]> {
|
||||
try {
|
||||
const metadataPath = await this.getMetadataPath(identifier);
|
||||
const metadataLink = await this.getMetadataLink(identifier);
|
||||
|
||||
// Check if the metadata file exists first
|
||||
await fsPromises.lstat(metadataPath);
|
||||
await fsPromises.lstat(metadataLink.filePath);
|
||||
|
||||
const readMetadataStream = guardStream(createReadStream(metadataPath));
|
||||
return await parseQuads(readMetadataStream);
|
||||
const readMetadataStream = guardStream(createReadStream(metadataLink.filePath));
|
||||
return await parseQuads(readMetadataStream, metadataLink.contentType);
|
||||
} catch (error: unknown) {
|
||||
// Metadata file doesn't exist so lets keep `rawMetaData` an empty array.
|
||||
if (!isSystemError(error) || error.code !== 'ENOENT') {
|
||||
|
@ -3,7 +3,6 @@ import arrayifyStream from 'arrayify-stream';
|
||||
import { DataFactory, StreamParser, StreamWriter } from 'n3';
|
||||
import type { Literal, NamedNode, Quad } from 'rdf-js';
|
||||
import streamifyArray from 'streamify-array';
|
||||
import { TEXT_TURTLE } from './ContentTypes';
|
||||
import type { Guarded } from './GuardedStream';
|
||||
import { pipeSafely } from './StreamUtil';
|
||||
|
||||
@ -17,17 +16,19 @@ export const pushQuad =
|
||||
/**
|
||||
* Helper function for serializing an array of quads, with as result a Readable object.
|
||||
* @param quads - The array of quads.
|
||||
* @param contentType - The content-type to serialize to.
|
||||
*
|
||||
* @returns The Readable object.
|
||||
*/
|
||||
export const serializeQuads = (quads: Quad[]): Guarded<Readable> =>
|
||||
pipeSafely(streamifyArray(quads), new StreamWriter({ format: TEXT_TURTLE }));
|
||||
export const serializeQuads = (quads: Quad[], contentType?: string): Guarded<Readable> =>
|
||||
pipeSafely(streamifyArray(quads), new StreamWriter({ format: contentType }));
|
||||
|
||||
/**
|
||||
* Helper function to convert a Readable into an array of quads.
|
||||
* @param readable - The readable object.
|
||||
* @param contentType - The content-type of the stream.
|
||||
*
|
||||
* @returns A promise containing the array of quads.
|
||||
*/
|
||||
export const parseQuads = async(readable: Guarded<Readable>): Promise<Quad[]> =>
|
||||
arrayifyStream(pipeSafely(readable, new StreamParser({ format: TEXT_TURTLE })));
|
||||
export const parseQuads = async(readable: Guarded<Readable>, contentType?: string): Promise<Quad[]> =>
|
||||
arrayifyStream(pipeSafely(readable, new StreamParser({ format: contentType })));
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'jest-rdf';
|
||||
import { DataFactory } from 'n3';
|
||||
import type { Quad } from 'rdf-js';
|
||||
import { pushQuad } from '../../../src/util/QuadUtil';
|
||||
import { parseQuads, pushQuad, serializeQuads } from '../../../src/util/QuadUtil';
|
||||
import { guardedStreamFrom, readableToString } from '../../../src/util/StreamUtil';
|
||||
|
||||
describe('QuadUtil', (): void => {
|
||||
describe('#pushQuad', (): void => {
|
||||
@ -13,4 +14,27 @@ describe('QuadUtil', (): void => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#serializeQuads', (): void => {
|
||||
it('converts quads to the requested format.', async(): Promise<void> => {
|
||||
const quads = [ DataFactory.quad(
|
||||
DataFactory.namedNode('pre:sub'),
|
||||
DataFactory.namedNode('pre:pred'),
|
||||
DataFactory.literal('obj'),
|
||||
) ];
|
||||
const stream = serializeQuads(quads, 'application/n-triples');
|
||||
await expect(readableToString(stream)).resolves.toMatch('<pre:sub> <pre:pred> "obj" .');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#parseQuads', (): void => {
|
||||
it('parses quads from the requested format.', async(): Promise<void> => {
|
||||
const stream = guardedStreamFrom([ '<pre:sub> <pre:pred> "obj".' ]);
|
||||
await expect(parseQuads(stream, 'application/n-triples')).resolves.toEqualRdfQuadArray([ DataFactory.quad(
|
||||
DataFactory.namedNode('pre:sub'),
|
||||
DataFactory.namedNode('pre:pred'),
|
||||
DataFactory.literal('obj'),
|
||||
) ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user