Skip to content

Commit

Permalink
refactor: Streamline RepresentationMetadata interface
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimvh committed Sep 16, 2020
1 parent 76319ba commit 8d39793
Show file tree
Hide file tree
Showing 36 changed files with 416 additions and 230 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.js
Expand Up @@ -27,15 +27,18 @@ module.exports = {
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/no-unnecessary-condition': 'off', // problems with optional parameters
'@typescript-eslint/space-before-function-paren': [ 'error', 'never' ],
'@typescript-eslint/unified-signatures': 'off',
'class-methods-use-this': 'off', // conflicts with functions from interfaces that sometimes don't require `this`
'comma-dangle': ['error', 'always-multiline'],
'dot-location': ['error', 'property'],
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'max-len': ['error', { code: 120, ignoreUrls: true }],
'no-param-reassign': 'off', // necessary in constructor overloading
'no-underscore-dangle': 'off', // conflicts with external libraries
'padding-line-between-statements': 'off',
'tsdoc/syntax': 'error',
'prefer-named-capture-group': 'off',
'tsdoc/syntax': 'error',
'unicorn/no-fn-reference-in-iterator': 'off', // this prevents some functional programming paradigms

// Import
'sort-imports': 'off', // Disabled in favor of eslint-plugin-import
Expand Down
9 changes: 4 additions & 5 deletions src/init/Setup.ts
Expand Up @@ -4,7 +4,7 @@ import { RepresentationMetadata } from '../ldp/representation/RepresentationMeta
import { ExpressHttpServer } from '../server/ExpressHttpServer';
import { ResourceStore } from '../storage/ResourceStore';
import { TEXT_TURTLE } from '../util/ContentTypes';
import { CONTENT_TYPE } from '../util/MetadataTypes';
import { MA_CONTENT_TYPE } from '../util/MetadataTypes';

/**
* Invokes all logic to setup a server.
Expand Down Expand Up @@ -50,11 +50,10 @@ export class Setup {
acl:mode acl:Control;
acl:accessTo <${this.base}>;
acl:default <${this.base}>.`;
const aclId = await this.aclManager.getAcl({ path: this.base });
const metadata = new RepresentationMetadata(aclId.path);
metadata.set(CONTENT_TYPE, TEXT_TURTLE);
const baseAclId = await this.aclManager.getAcl({ path: this.base });
const metadata = new RepresentationMetadata(baseAclId.path, { [MA_CONTENT_TYPE]: TEXT_TURTLE });
await this.store.setRepresentation(
aclId,
baseAclId,
{
binary: true,
data: streamifyArray([ acl ]),
Expand Down
3 changes: 1 addition & 2 deletions src/ldp/http/BasicResponseWriter.ts
@@ -1,7 +1,6 @@
import { HttpResponse } from '../../server/HttpResponse';
import { HttpError } from '../../util/errors/HttpError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { CONTENT_TYPE } from '../../util/MetadataTypes';
import { ResponseDescription } from '../operations/ResponseDescription';
import { ResponseWriter } from './ResponseWriter';

Expand Down Expand Up @@ -30,7 +29,7 @@ export class BasicResponseWriter extends ResponseWriter {
} else {
input.response.setHeader('location', input.result.identifier.path);
if (input.result.body) {
const contentType = input.result.body.metadata.get(CONTENT_TYPE)?.value ?? 'text/plain';
const contentType = input.result.body.metadata.contentType ?? 'text/plain';
input.response.setHeader('content-type', contentType);
input.result.body.data.pipe(input.response);
}
Expand Down
14 changes: 7 additions & 7 deletions src/ldp/http/RawBodyParser.ts
@@ -1,6 +1,6 @@
import { HttpRequest } from '../../server/HttpRequest';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { CONTENT_TYPE, SLUG, TYPE } from '../../util/MetadataTypes';
import { HTTP_SLUG, RDF_TYPE, MA_CONTENT_TYPE } from '../../util/MetadataTypes';
import { Representation } from '../representation/Representation';
import { RepresentationMetadata } from '../representation/RepresentationMetadata';
import { BodyParser } from './BodyParser';
Expand Down Expand Up @@ -40,16 +40,15 @@ export class RawBodyParser extends BodyParser {
private parseMetadata(input: HttpRequest): RepresentationMetadata {
const contentType = /^[^;]*/u.exec(input.headers['content-type']!)![0];

const metadata: RepresentationMetadata = new RepresentationMetadata();
metadata.set(CONTENT_TYPE, contentType);
const metadata = new RepresentationMetadata({ [MA_CONTENT_TYPE]: contentType });

const { link, slug } = input.headers;

if (slug) {
if (Array.isArray(slug)) {
throw new UnsupportedHttpError('At most 1 slug header is allowed.');
}
metadata.set(SLUG, slug);
metadata.set(HTTP_SLUG, slug);
}

// There are similarities here to Accept header parsing so that library should become more generic probably
Expand All @@ -60,11 +59,12 @@ export class RawBodyParser extends BodyParser {
const [ , rel ] = /^ *; *rel="(.*)"$/u.exec(rest) ?? [];
return { url, rel };
});
parsedLinks.forEach((entry): void => {
for (const entry of parsedLinks) {
if (entry.rel === 'type') {
metadata.set(TYPE, entry.url);
metadata.set(RDF_TYPE, entry.url);
break;
}
});
}
}

return metadata;
Expand Down
5 changes: 2 additions & 3 deletions src/ldp/http/SparqlUpdateBodyParser.ts
Expand Up @@ -3,7 +3,7 @@ import { translate } from 'sparqlalgebrajs';
import { HttpRequest } from '../../server/HttpRequest';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';
import { CONTENT_TYPE } from '../../util/MetadataTypes';
import { MA_CONTENT_TYPE } from '../../util/MetadataTypes';
import { readableToString } from '../../util/Util';
import { RepresentationMetadata } from '../representation/RepresentationMetadata';
import { BodyParser } from './BodyParser';
Expand Down Expand Up @@ -35,8 +35,7 @@ export class SparqlUpdateBodyParser extends BodyParser {
const sparql = await readableToString(toAlgebraStream);
const algebra = translate(sparql, { quads: true });

const metadata = new RepresentationMetadata();
metadata.add(CONTENT_TYPE, 'application/sparql-update');
const metadata = new RepresentationMetadata({ [MA_CONTENT_TYPE]: 'application/sparql-update' });

// Prevent body from being requested again
return {
Expand Down
41 changes: 41 additions & 0 deletions src/ldp/representation/MetadataUtil.ts
@@ -0,0 +1,41 @@
import { literal, namedNode } from '@rdfjs/data-model';
import type { Literal, NamedNode, Term } from 'rdf-js';
import { MA_CONTENT_TYPE } from '../../util/MetadataTypes';

// Shorthands for commonly used predicates
const shorthands: { [id: string]: NamedNode } = {
contentType: namedNode(MA_CONTENT_TYPE),
};

// Caches named node conversions
const termMap: { [id: string]: NamedNode } = {};

/**
* @param input - Checks if this is a {@link Term}.
*/
export const isTerm = (input?: any): input is Term => input?.termType;

/**
* Converts the incoming predicate to a named node.
* In case of string, first checks if it is a shorthand, if not a new named node gets made.
* @param predicate - Predicate to potentially transform.
*/
export const getPredicateTerm = (predicate: NamedNode | string): NamedNode => {
if (typeof predicate === 'string') {
if (shorthands[predicate]) {
return shorthands[predicate];
}
if (!termMap[predicate]) {
termMap[predicate] = namedNode(predicate);
}
return termMap[predicate];
}
return predicate;
};

/**
* Converts an object to a literal when needed.
* @param object - Object to potentially transform.
*/
export const getObjectTerm = (object: NamedNode | Literal | string): NamedNode | Literal =>
typeof object === 'string' ? literal(object) : object;

0 comments on commit 8d39793

Please sign in to comment.