refactor: Move file related metadata to FileResourceStore

This commit is contained in:
Joachim Van Herwegen 2020-09-23 17:05:59 +02:00
parent 22962192ff
commit fa935cc4c7
3 changed files with 55 additions and 30 deletions

View File

@ -3,7 +3,7 @@ import { createReadStream, createWriteStream, promises as fsPromises } from 'fs'
import { posix } from 'path';
import type { Readable } from 'stream';
import { DataFactory } from 'n3';
import type { Quad } from 'rdf-js';
import type { NamedNode, Quad } from 'rdf-js';
import streamifyArray from 'streamify-array';
import type { Representation } from '../ldp/representation/Representation';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
@ -16,8 +16,8 @@ import { UnsupportedMediaTypeHttpError } from '../util/errors/UnsupportedMediaTy
import type { InteractionController } from '../util/InteractionController';
import type { MetadataController } from '../util/MetadataController';
import { CONTENT_TYPE, DCTERMS, HTTP, POSIX, RDF, XSD } from '../util/UriConstants';
import { toTypedLiteral } from '../util/UriUtil';
import { ensureTrailingSlash } from '../util/Util';
import { toNamedNode, toTypedLiteral } from '../util/UriUtil';
import { ensureTrailingSlash, pushQuad } from '../util/Util';
import type { ExtensionBasedMapper } from './ExtensionBasedMapper';
import type { ResourceLink } from './FileIdentifierMapper';
import type { ResourceStore } from './ResourceStore';
@ -244,11 +244,11 @@ export class FileResourceStore implements ResourceStore {
*/
private async getDirectoryRepresentation(resourceLink: ResourceLink, stats: Stats): Promise<Representation> {
const files = await fsPromises.readdir(resourceLink.filePath);
const quads: Quad[] = [];
const containerURI = resourceLink.identifier.path;
const containerURI = DataFactory.namedNode(resourceLink.identifier.path);
quads.push(...this.metadataController.generateResourceQuads(containerURI, stats));
const quads = this.metadataController.generateResourceQuads(containerURI, true);
quads.push(...this.generatePosixQuads(containerURI, stats));
quads.push(...await this.getDirChildrenQuadRepresentation(files, resourceLink.filePath, containerURI));
let rawMetadata: Quad[] = [];
@ -259,9 +259,11 @@ export class FileResourceStore implements ResourceStore {
// Metadata file doesn't exist so lets keep `rawMetaData` an empty array.
}
const metadata = new RepresentationMetadata(containerURI).addQuads(rawMetadata)
.set(DCTERMS.modified, toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime))
.set(CONTENT_TYPE, INTERNAL_QUADS);
const metadata = new RepresentationMetadata(containerURI, {
[DCTERMS.modified]: toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime),
[CONTENT_TYPE]: INTERNAL_QUADS,
});
metadata.addQuads(rawMetadata);
return {
binary: false,
@ -278,7 +280,8 @@ export class FileResourceStore implements ResourceStore {
*
* @returns A promise containing all quads.
*/
private async getDirChildrenQuadRepresentation(files: string[], path: string, containerURI: string): Promise<Quad[]> {
private async getDirChildrenQuadRepresentation(files: string[], path: string, containerURI: NamedNode):
Promise<Quad[]> {
const quads: Quad[] = [];
const childURIs: string[] = [];
for (const childName of files) {
@ -290,7 +293,9 @@ export class FileResourceStore implements ResourceStore {
const childLink = await this.resourceMapper
.mapFilePathToUrl(joinPath(path, childName), childStats.isDirectory());
quads.push(...this.metadataController.generateResourceQuads(childLink.identifier.path, childStats));
const subject = DataFactory.namedNode(childLink.identifier.path);
quads.push(...this.metadataController.generateResourceQuads(subject, childStats.isDirectory()));
quads.push(...this.generatePosixQuads(subject, childStats));
childURIs.push(childLink.identifier.path);
} catch {
// Skip the child if there is an error.
@ -302,6 +307,21 @@ export class FileResourceStore implements ResourceStore {
return quads.concat(containsQuads);
}
/**
* Helper function to add file system related metadata
* @param subject - Subject for the new quads.
* @param stats - Stats of the file/directory corresponding to the resource.
*/
private generatePosixQuads(subject: NamedNode, stats: Stats): Quad[] {
const quads: Quad[] = [];
pushQuad(quads, subject, toNamedNode(POSIX.size), toTypedLiteral(stats.size, XSD.integer));
pushQuad(quads, subject, toNamedNode(DCTERMS.modified), toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime));
pushQuad(quads, subject, toNamedNode(POSIX.mtime), toTypedLiteral(
Math.floor(stats.mtime.getTime() / 1000), XSD.integer,
));
return quads;
}
/**
* Helper function to (re)write file for the resource if no container with that identifier exists.
* @param path - The path to the directory of the file.

View File

@ -1,35 +1,31 @@
import type { Stats } from 'fs';
import type { Readable } from 'stream';
import arrayifyStream from 'arrayify-stream';
import { DataFactory, StreamParser, StreamWriter } from 'n3';
import type { Quad } from 'rdf-js';
import type { NamedNode, Quad } from 'rdf-js';
import streamifyArray from 'streamify-array';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
import { TEXT_TURTLE } from './ContentTypes';
import { DCTERMS, LDP, POSIX, RDF, XSD } from './UriConstants';
import { toNamedNode, toTypedLiteral } from './UriUtil';
import { pipeStreamsAndErrors } from './Util';
import { LDP, RDF } from './UriConstants';
import { toNamedNode } from './UriUtil';
import { pipeStreamsAndErrors, pushQuad } from './Util';
export class MetadataController {
/**
* Helper function to generate quads for a Container or Resource.
* @param uri - The URI for which the quads should be generated.
* @param stats - The Stats of the subject.
* Helper function to generate type quads for a Container or Resource.
* @param subject - Subject for the new quads.
* @param isContainer - If the identifier corresponds to a container.
*
* @returns The generated quads.
*/
public generateResourceQuads(uri: string, stats: Stats): Quad[] {
const metadata = new RepresentationMetadata(uri);
if (stats.isDirectory()) {
metadata.add(RDF.type, toNamedNode(LDP.Container));
metadata.add(RDF.type, toNamedNode(LDP.BasicContainer));
public generateResourceQuads(subject: NamedNode, isContainer: boolean): Quad[] {
const quads: Quad[] = [];
if (isContainer) {
pushQuad(quads, subject, toNamedNode(RDF.type), toNamedNode(LDP.Container));
pushQuad(quads, subject, toNamedNode(RDF.type), toNamedNode(LDP.BasicContainer));
}
metadata.add(RDF.type, toNamedNode(LDP.Resource));
metadata.add(POSIX.size, toTypedLiteral(stats.size, XSD.integer));
metadata.add(DCTERMS.modified, toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime));
metadata.add(POSIX.mtime, toTypedLiteral(Math.floor(stats.mtime.getTime() / 1000), XSD.integer));
pushQuad(quads, subject, toNamedNode(RDF.type), toNamedNode(LDP.Resource));
return metadata.quads();
return quads;
}
/**
@ -39,7 +35,7 @@ export class MetadataController {
*
* @returns The generated quads.
*/
public generateContainerContainsResourceQuads(containerURI: string, childURIs: string[]): Quad[] {
public generateContainerContainsResourceQuads(containerURI: NamedNode, childURIs: string[]): Quad[] {
return new RepresentationMetadata(containerURI, { [LDP.contains]: childURIs.map(DataFactory.namedNode) }).quads();
}

View File

@ -1,5 +1,7 @@
import type { Readable, Writable } from 'stream';
import arrayifyStream from 'arrayify-stream';
import { DataFactory } from 'n3';
import type { Literal, NamedNode, Quad } from 'rdf-js';
import { UnsupportedHttpError } from './errors/UnsupportedHttpError';
/**
@ -84,3 +86,10 @@ export const decodeUriPathComponents = (path: string): string => path.split('/')
* Encodes all (non-slash) special characters in a URI path.
*/
export const encodeUriPathComponents = (path: string): string => path.split('/').map(encodeURIComponent).join('/');
/**
* Generates a quad with the given subject/predicate/object and pushes it to the given array.
*/
export const pushQuad =
(quads: Quad[], subject: NamedNode, predicate: NamedNode, object: NamedNode | Literal): number =>
quads.push(DataFactory.quad(subject, predicate, object));