@solid/community-server
Version:
Community Solid Server: an open and modular implementation of the Solid specifications
135 lines • 5.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InMemoryDataAccessor = void 0;
const RepresentationMetadata_1 = require("../../http/representation/RepresentationMetadata");
const InternalServerError_1 = require("../../util/errors/InternalServerError");
const NotFoundHttpError_1 = require("../../util/errors/NotFoundHttpError");
const StreamUtil_1 = require("../../util/StreamUtil");
const Vocabularies_1 = require("../../util/Vocabularies");
const ConversionUtil_1 = require("../conversion/ConversionUtil");
class InMemoryDataAccessor {
identifierStrategy;
// A dummy container where every entry corresponds to a root container
store;
constructor(identifierStrategy) {
this.identifierStrategy = identifierStrategy;
this.store = { entries: {} };
}
async canHandle() {
// All data is supported since streams never get read, only copied
}
async getData(identifier) {
const entry = this.getEntry(identifier);
if (!this.isDataEntry(entry)) {
throw new NotFoundHttpError_1.NotFoundHttpError();
}
return (0, StreamUtil_1.guardedStreamFrom)(entry.data);
}
async getMetadata(identifier) {
const entry = this.getEntry(identifier);
return new RepresentationMetadata_1.RepresentationMetadata(entry.metadata);
}
async *getChildren(identifier) {
const entry = this.getEntry(identifier);
if (!this.isDataEntry(entry)) {
const childNames = Object.keys(entry.entries);
yield* childNames.map((name) => new RepresentationMetadata_1.RepresentationMetadata({ path: name }));
}
}
async writeDocument(identifier, data, metadata) {
const parent = this.getParentEntry(identifier);
// Drain original stream and create copy
const dataArray = await (0, StreamUtil_1.arrayifyStream)(data);
// Only add the size for binary streams, which are all streams that do not have an internal type.
if (metadata.contentType && !(0, ConversionUtil_1.isInternalContentType)(metadata.contentType)) {
const size = dataArray.reduce((total, chunk) => total + chunk.length, 0);
metadata.set(Vocabularies_1.POSIX.terms.size, `${size}`);
}
parent.entries[identifier.path] = {
data: dataArray,
metadata,
};
}
async writeContainer(identifier, metadata) {
try {
// Overwrite existing metadata but keep children if container already exists
const entry = this.getEntry(identifier);
entry.metadata = metadata;
}
catch (error) {
// Create new entry if it didn't exist yet
if (NotFoundHttpError_1.NotFoundHttpError.isInstance(error)) {
const parent = this.getParentEntry(identifier);
parent.entries[identifier.path] = {
entries: {},
metadata,
};
}
else {
throw error;
}
}
}
async writeMetadata(identifier, metadata) {
const entry = this.getEntry(identifier);
entry.metadata = metadata;
}
async deleteResource(identifier) {
const parent = this.getParentEntry(identifier);
if (!parent.entries[identifier.path]) {
throw new NotFoundHttpError_1.NotFoundHttpError();
}
delete parent.entries[identifier.path];
}
isDataEntry(entry) {
return Boolean(entry.data);
}
/**
* Generates an array of identifiers corresponding to the nested containers until the given identifier is reached.
* This does not verify if these identifiers actually exist.
*/
getHierarchy(identifier) {
if (this.identifierStrategy.isRootContainer(identifier)) {
return [identifier];
}
const hierarchy = this.getHierarchy(this.identifierStrategy.getParentContainer(identifier));
hierarchy.push(identifier);
return hierarchy;
}
/**
* Returns the ContainerEntry corresponding to the parent container of the given identifier.
* Will throw 404 if the parent does not exist.
*/
getParentEntry(identifier) {
// Casting is fine here as the parent should never be used as a real container
let parent = this.store;
if (this.identifierStrategy.isRootContainer(identifier)) {
return parent;
}
const hierarchy = this.getHierarchy(this.identifierStrategy.getParentContainer(identifier));
for (const entry of hierarchy) {
parent = parent.entries[entry.path];
if (!parent) {
throw new NotFoundHttpError_1.NotFoundHttpError();
}
if (this.isDataEntry(parent)) {
throw new InternalServerError_1.InternalServerError('Invalid path.');
}
}
return parent;
}
/**
* Returns the CacheEntry corresponding the given identifier.
* Will throw 404 if the resource does not exist.
*/
getEntry(identifier) {
const parent = this.getParentEntry(identifier);
const entry = parent.entries[identifier.path];
if (!entry) {
throw new NotFoundHttpError_1.NotFoundHttpError();
}
return entry;
}
}
exports.InMemoryDataAccessor = InMemoryDataAccessor;
//# sourceMappingURL=InMemoryDataAccessor.js.map