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 { posix } from 'path';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
import { DataFactory } from 'n3'; import { DataFactory } from 'n3';
import type { Quad } from 'rdf-js'; import type { NamedNode, Quad } from 'rdf-js';
import streamifyArray from 'streamify-array'; import streamifyArray from 'streamify-array';
import type { Representation } from '../ldp/representation/Representation'; import type { Representation } from '../ldp/representation/Representation';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata'; import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
@ -16,8 +16,8 @@ import { UnsupportedMediaTypeHttpError } from '../util/errors/UnsupportedMediaTy
import type { InteractionController } from '../util/InteractionController'; import type { InteractionController } from '../util/InteractionController';
import type { MetadataController } from '../util/MetadataController'; import type { MetadataController } from '../util/MetadataController';
import { CONTENT_TYPE, DCTERMS, HTTP, POSIX, RDF, XSD } from '../util/UriConstants'; import { CONTENT_TYPE, DCTERMS, HTTP, POSIX, RDF, XSD } from '../util/UriConstants';
import { toTypedLiteral } from '../util/UriUtil'; import { toNamedNode, toTypedLiteral } from '../util/UriUtil';
import { ensureTrailingSlash } from '../util/Util'; import { ensureTrailingSlash, pushQuad } from '../util/Util';
import type { ExtensionBasedMapper } from './ExtensionBasedMapper'; import type { ExtensionBasedMapper } from './ExtensionBasedMapper';
import type { ResourceLink } from './FileIdentifierMapper'; import type { ResourceLink } from './FileIdentifierMapper';
import type { ResourceStore } from './ResourceStore'; import type { ResourceStore } from './ResourceStore';
@ -244,11 +244,11 @@ export class FileResourceStore implements ResourceStore {
*/ */
private async getDirectoryRepresentation(resourceLink: ResourceLink, stats: Stats): Promise<Representation> { private async getDirectoryRepresentation(resourceLink: ResourceLink, stats: Stats): Promise<Representation> {
const files = await fsPromises.readdir(resourceLink.filePath); 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)); quads.push(...await this.getDirChildrenQuadRepresentation(files, resourceLink.filePath, containerURI));
let rawMetadata: Quad[] = []; let rawMetadata: Quad[] = [];
@ -259,9 +259,11 @@ export class FileResourceStore implements ResourceStore {
// Metadata file doesn't exist so lets keep `rawMetaData` an empty array. // Metadata file doesn't exist so lets keep `rawMetaData` an empty array.
} }
const metadata = new RepresentationMetadata(containerURI).addQuads(rawMetadata) const metadata = new RepresentationMetadata(containerURI, {
.set(DCTERMS.modified, toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime)) [DCTERMS.modified]: toTypedLiteral(stats.mtime.toISOString(), XSD.dateTime),
.set(CONTENT_TYPE, INTERNAL_QUADS); [CONTENT_TYPE]: INTERNAL_QUADS,
});
metadata.addQuads(rawMetadata);
return { return {
binary: false, binary: false,
@ -278,7 +280,8 @@ export class FileResourceStore implements ResourceStore {
* *
* @returns A promise containing all quads. * @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 quads: Quad[] = [];
const childURIs: string[] = []; const childURIs: string[] = [];
for (const childName of files) { for (const childName of files) {
@ -290,7 +293,9 @@ export class FileResourceStore implements ResourceStore {
const childLink = await this.resourceMapper const childLink = await this.resourceMapper
.mapFilePathToUrl(joinPath(path, childName), childStats.isDirectory()); .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); childURIs.push(childLink.identifier.path);
} catch { } catch {
// Skip the child if there is an error. // Skip the child if there is an error.
@ -302,6 +307,21 @@ export class FileResourceStore implements ResourceStore {
return quads.concat(containsQuads); 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. * 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. * @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 type { Readable } from 'stream';
import arrayifyStream from 'arrayify-stream'; import arrayifyStream from 'arrayify-stream';
import { DataFactory, StreamParser, StreamWriter } from 'n3'; 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 streamifyArray from 'streamify-array';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata'; import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
import { TEXT_TURTLE } from './ContentTypes'; import { TEXT_TURTLE } from './ContentTypes';
import { DCTERMS, LDP, POSIX, RDF, XSD } from './UriConstants'; import { LDP, RDF } from './UriConstants';
import { toNamedNode, toTypedLiteral } from './UriUtil'; import { toNamedNode } from './UriUtil';
import { pipeStreamsAndErrors } from './Util'; import { pipeStreamsAndErrors, pushQuad } from './Util';
export class MetadataController { export class MetadataController {
/** /**
* Helper function to generate quads for a Container or Resource. * Helper function to generate type quads for a Container or Resource.
* @param uri - The URI for which the quads should be generated. * @param subject - Subject for the new quads.
* @param stats - The Stats of the subject. * @param isContainer - If the identifier corresponds to a container.
* *
* @returns The generated quads. * @returns The generated quads.
*/ */
public generateResourceQuads(uri: string, stats: Stats): Quad[] { public generateResourceQuads(subject: NamedNode, isContainer: boolean): Quad[] {
const metadata = new RepresentationMetadata(uri); const quads: Quad[] = [];
if (stats.isDirectory()) { if (isContainer) {
metadata.add(RDF.type, toNamedNode(LDP.Container)); pushQuad(quads, subject, toNamedNode(RDF.type), toNamedNode(LDP.Container));
metadata.add(RDF.type, toNamedNode(LDP.BasicContainer)); pushQuad(quads, subject, toNamedNode(RDF.type), toNamedNode(LDP.BasicContainer));
} }
metadata.add(RDF.type, toNamedNode(LDP.Resource)); pushQuad(quads, subject, toNamedNode(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));
return metadata.quads(); return quads;
} }
/** /**
@ -39,7 +35,7 @@ export class MetadataController {
* *
* @returns The generated quads. * @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(); return new RepresentationMetadata(containerURI, { [LDP.contains]: childURIs.map(DataFactory.namedNode) }).quads();
} }

View File

@ -1,5 +1,7 @@
import type { Readable, Writable } from 'stream'; import type { Readable, Writable } from 'stream';
import arrayifyStream from 'arrayify-stream'; import arrayifyStream from 'arrayify-stream';
import { DataFactory } from 'n3';
import type { Literal, NamedNode, Quad } from 'rdf-js';
import { UnsupportedHttpError } from './errors/UnsupportedHttpError'; 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. * Encodes all (non-slash) special characters in a URI path.
*/ */
export const encodeUriPathComponents = (path: string): string => path.split('/').map(encodeURIComponent).join('/'); 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));