refactor: Remove RuntimeConfig in favor of config variables, Closes #106

This commit is contained in:
Ruben Taelman 2020-09-11 10:18:53 +02:00 committed by Joachim Van Herwegen
parent b1991cb08a
commit 1dd140ab61
25 changed files with 116 additions and 199 deletions

View File

@ -10,6 +10,7 @@
"files-scs:config/presets/ldp/request-parser.json", "files-scs:config/presets/ldp/request-parser.json",
"files-scs:config/presets/setup.json", "files-scs:config/presets/setup.json",
"files-scs:config/presets/storage.json", "files-scs:config/presets/storage.json",
"files-scs:config/presets/storage_wrapper.json" "files-scs:config/presets/storage_wrapper.json",
"files-scs:config/presets/cli-params.json"
] ]
} }

View File

@ -0,0 +1,17 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:default:variable:port",
"@type": "Variable"
},
{
"@id": "urn:solid-server:default:variable:base",
"@type": "Variable"
},
{
"@id": "urn:solid-server:default:variable:rootFilePath",
"@type": "Variable"
}
]
}

View File

@ -13,10 +13,11 @@
"Setup:_aclManager": { "Setup:_aclManager": {
"@id": "urn:solid-server:default:AclManager" "@id": "urn:solid-server:default:AclManager"
}, },
"Setup:_runtimeConfig": { "Setup:_base": {
"@id": "urn:solid-server:default:RuntimeConfig", "@id": "urn:solid-server:default:variable:base"
"@type": "RuntimeConfig", },
"RuntimeConfigData:_port": 3000 "Setup:_port": {
"@id": "urn:solid-server:default:variable:port"
} }
} }
] ]

View File

@ -4,8 +4,8 @@
{ {
"@id": "urn:solid-server:default:ResourceStore", "@id": "urn:solid-server:default:ResourceStore",
"@type": "InMemoryResourceStore", "@type": "InMemoryResourceStore",
"InMemoryResourceStore:_runtimeConfig": { "InMemoryResourceStore:_base": {
"@id": "urn:solid-server:default:RuntimeConfig" "@id": "urn:solid-server:default:variable:base"
} }
} }
] ]

View File

@ -72,8 +72,8 @@
{ {
"@id": "urn:solid-server:default:UrlContainerManager", "@id": "urn:solid-server:default:UrlContainerManager",
"@type": "UrlContainerManager", "@type": "UrlContainerManager",
"UrlContainerManager:_runtimeConfig": { "UrlContainerManager:_base": {
"@id": "urn:solid-server:default:RuntimeConfig" "@id": "urn:solid-server:default:variable:base"
} }
} }
] ]

View File

@ -12,7 +12,6 @@ export * from './src/authorization/WebAclAuthorizer';
// Init // Init
export * from './src/init/CliRunner'; export * from './src/init/CliRunner';
export * from './src/init/RuntimeConfig';
export * from './src/init/Setup'; export * from './src/init/Setup';
// LDP/HTTP // LDP/HTTP

6
package-lock.json generated
View File

@ -2519,9 +2519,9 @@
} }
}, },
"componentsjs": { "componentsjs": {
"version": "3.4.2", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/componentsjs/-/componentsjs-3.4.2.tgz", "resolved": "https://registry.npmjs.org/componentsjs/-/componentsjs-3.6.0.tgz",
"integrity": "sha512-ICaSvvxY/CvWwFt0lLsoRL/DHY09akoI6x9WakQQ9g4GYHVaZumWSAdOrzM/htjGpBumpVh1C/4hk0/ghh9jXA==", "integrity": "sha512-G3lMrIbE7iiZpERoPXnxM0aDopq9q1s1C5aIUrnHW3rcRDa3kcCytc4ASt5aFRjNiwBubivcMfJsvF2ihXg7jQ==",
"requires": { "requires": {
"@types/lodash": "^4.14.56", "@types/lodash": "^4.14.56",
"@types/minimist": "^1.2.0", "@types/minimist": "^1.2.0",

View File

@ -42,7 +42,7 @@
"prepare": "npm run build", "prepare": "npm run build",
"start": "node ./bin/server.js -p 3000", "start": "node ./bin/server.js -p 3000",
"test": "jest", "test": "jest",
"validate": "componentsjs-compile-config urn:solid-server:default -c config/config-default.json > /dev/null", "validate": "componentsjs-compile-config urn:solid-server:default -c config/config-default.json -f > /dev/null",
"version": "manual-git-changelog onversion", "version": "manual-git-changelog onversion",
"watch": "nodemon --watch \"src/**/*.js\" --watch \"bin/**/*.js\" --exec npm start" "watch": "nodemon --watch \"src/**/*.js\" --watch \"bin/**/*.js\" --exec npm start"
}, },
@ -76,7 +76,7 @@
"@types/yargs": "^15.0.5", "@types/yargs": "^15.0.5",
"arrayify-stream": "^1.0.0", "arrayify-stream": "^1.0.0",
"async-lock": "^1.2.4", "async-lock": "^1.2.4",
"componentsjs": "^3.4.2", "componentsjs": "^3.6.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",

View File

@ -2,7 +2,6 @@ import * as Path from 'path';
import { ReadStream, WriteStream } from 'tty'; import { ReadStream, WriteStream } from 'tty';
import { Loader, LoaderProperties } from 'componentsjs'; import { Loader, LoaderProperties } from 'componentsjs';
import yargs from 'yargs'; import yargs from 'yargs';
import { RuntimeConfig } from './RuntimeConfig';
import { Setup } from './Setup'; import { Setup } from './Setup';
/** /**
@ -23,12 +22,12 @@ export const runCustom = function(
const { argv } = yargs const { argv } = yargs
.usage('node ./bin/server.js [args]') .usage('node ./bin/server.js [args]')
.options({ .options({
port: { type: 'number', alias: 'p' }, port: { type: 'number', alias: 'p', default: 3000 },
config: { type: 'string', alias: 'c' }, config: { type: 'string', alias: 'c' },
}) })
.help(); .help();
new Promise<RuntimeConfig>(async(resolve): Promise<void> => { (async(): Promise<string> => {
// Load provided or default config file // Load provided or default config file
const configPath = argv.config ? const configPath = argv.config ?
Path.join(process.cwd(), argv.config) : Path.join(process.cwd(), argv.config) :
@ -38,10 +37,16 @@ export const runCustom = function(
const loader = new Loader(properties); const loader = new Loader(properties);
await loader.registerAvailableModuleResources(); await loader.registerAvailableModuleResources();
const setup: Setup = await loader const setup: Setup = await loader
.instantiateFromUrl('urn:solid-server:default', configPath); .instantiateFromUrl('urn:solid-server:default', configPath, undefined, {
resolve(await setup.setup({ port: argv.port })); variables: {
}).then((runtimeConfig: RuntimeConfig): void => { 'urn:solid-server:default:variable:port': argv.port,
stdout.write(`Running at ${runtimeConfig.base}\n`); 'urn:solid-server:default:variable:base': `http://localhost:${argv.port}/`,
'urn:solid-server:default:variable:rootFilePath': process.cwd(),
},
});
return await setup.setup();
})().then((base: string): void => {
stdout.write(`Running at ${base}\n`);
}).catch((error): void => { }).catch((error): void => {
stderr.write(`${error}\n`); stderr.write(`${error}\n`);
}); });

View File

@ -1,43 +0,0 @@
import { ensureTrailingSlash } from '../util/Util';
/**
* This class holds all configuration options that can be defined by the user via the command line.
*
* Concretely, this contains data that is only relevant *after* dependency injection.
*/
export class RuntimeConfig implements RuntimeConfigData {
private pport!: number;
private pbase!: string;
private prootFilepath!: string;
public constructor(data: RuntimeConfigData = {}) {
this.reset(data);
}
public reset(data: RuntimeConfigData): void {
this.pport = data.port ?? 3000;
this.pbase = ensureTrailingSlash(data.base ?? `http://localhost:${this.port}/`);
this.prootFilepath = data.rootFilepath ?? process.cwd();
}
/**
* The returned URL is ensured to have a trailing slash.
*/
public get base(): string {
return this.pbase;
}
public get port(): number {
return this.pport;
}
public get rootFilepath(): string {
return this.prootFilepath;
}
}
export interface RuntimeConfigData {
port?: number;
base?: string;
rootFilepath?: string;
}

View File

@ -3,7 +3,6 @@ import { AclManager } from '../authorization/AclManager';
import { ExpressHttpServer } from '../server/ExpressHttpServer'; import { ExpressHttpServer } from '../server/ExpressHttpServer';
import { ResourceStore } from '../storage/ResourceStore'; import { ResourceStore } from '../storage/ResourceStore';
import { TEXT_TURTLE } from '../util/ContentTypes'; import { TEXT_TURTLE } from '../util/ContentTypes';
import { RuntimeConfig, RuntimeConfigData } from './RuntimeConfig';
/** /**
* Invokes all logic to setup a server. * Invokes all logic to setup a server.
@ -12,27 +11,27 @@ export class Setup {
private readonly httpServer: ExpressHttpServer; private readonly httpServer: ExpressHttpServer;
private readonly store: ResourceStore; private readonly store: ResourceStore;
private readonly aclManager: AclManager; private readonly aclManager: AclManager;
private readonly runtimeConfig: RuntimeConfig; private readonly base: string;
private readonly port: number;
public constructor( public constructor(
httpServer: ExpressHttpServer, httpServer: ExpressHttpServer,
store: ResourceStore, store: ResourceStore,
aclManager: AclManager, aclManager: AclManager,
runtimeConfig: RuntimeConfig, base: string,
port: number,
) { ) {
this.httpServer = httpServer; this.httpServer = httpServer;
this.store = store; this.store = store;
this.aclManager = aclManager; this.aclManager = aclManager;
this.runtimeConfig = runtimeConfig; this.base = base;
this.port = port;
} }
/** /**
* Set up a server at the given port and base URL. * Set up a server.
* @param data - Runtime config data.
*/ */
public async setup(data: RuntimeConfigData = {}): Promise<RuntimeConfig> { public async setup(): Promise<string> {
this.runtimeConfig.reset(data);
// Set up acl so everything can still be done by default // Set up acl so everything can still be done by default
// Note that this will need to be adapted to go through all the correct channels later on // Note that this will need to be adapted to go through all the correct channels later on
const aclSetup = async(): Promise<void> => { const aclSetup = async(): Promise<void> => {
@ -47,10 +46,10 @@ export class Setup {
acl:mode acl:Append; acl:mode acl:Append;
acl:mode acl:Delete; acl:mode acl:Delete;
acl:mode acl:Control; acl:mode acl:Control;
acl:accessTo <${this.runtimeConfig.base}>; acl:accessTo <${this.base}>;
acl:default <${this.runtimeConfig.base}>.`; acl:default <${this.base}>.`;
await this.store.setRepresentation( await this.store.setRepresentation(
await this.aclManager.getAcl({ path: this.runtimeConfig.base }), await this.aclManager.getAcl({ path: this.base }),
{ {
binary: true, binary: true,
data: streamifyArray([ acl ]), data: streamifyArray([ acl ]),
@ -64,8 +63,8 @@ export class Setup {
}; };
await aclSetup(); await aclSetup();
this.httpServer.listen(this.runtimeConfig.port); this.httpServer.listen(this.port);
return this.runtimeConfig; return this.base;
} }
} }

View File

@ -1,6 +1,5 @@
import { posix } from 'path'; import { posix } from 'path';
import { types } from 'mime-types'; import { types } from 'mime-types';
import { RuntimeConfig } from '../init/RuntimeConfig';
import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { APPLICATION_OCTET_STREAM, TEXT_TURTLE } from '../util/ContentTypes'; import { APPLICATION_OCTET_STREAM, TEXT_TURTLE } from '../util/ContentTypes';
import { ConflictHttpError } from '../util/errors/ConflictHttpError'; import { ConflictHttpError } from '../util/errors/ConflictHttpError';
@ -24,22 +23,22 @@ export interface ResourcePath {
} }
export class ExtensionBasedMapper implements FileIdentifierMapper { export class ExtensionBasedMapper implements FileIdentifierMapper {
private readonly runtimeConfig: RuntimeConfig; private readonly base: string;
private readonly prootFilepath: string;
private readonly types: Record<string, any>; private readonly types: Record<string, any>;
public constructor(runtimeConfig: RuntimeConfig, overrideTypes = { acl: TEXT_TURTLE, metadata: TEXT_TURTLE }) { public constructor(base: string, rootFilepath: string, overrideTypes = { acl: TEXT_TURTLE, metadata: TEXT_TURTLE }) {
this.runtimeConfig = runtimeConfig; this.base = base;
this.prootFilepath = rootFilepath;
this.types = { ...types, ...overrideTypes }; this.types = { ...types, ...overrideTypes };
} }
// Using getters because the values of runtimeConfig get filled in at runtime (so they are still empty at
// construction time until issue #106 gets resolved.)
public get baseRequestURI(): string { public get baseRequestURI(): string {
return trimTrailingSlashes(this.runtimeConfig.base); return trimTrailingSlashes(this.base);
} }
public get rootFilepath(): string { public get rootFilepath(): string {
return trimTrailingSlashes(this.runtimeConfig.rootFilepath); return trimTrailingSlashes(this.prootFilepath);
} }
/** /**

View File

@ -1,7 +1,6 @@
import { PassThrough } from 'stream'; import { PassThrough } from 'stream';
import arrayifyStream from 'arrayify-stream'; import arrayifyStream from 'arrayify-stream';
import streamifyArray from 'streamify-array'; import streamifyArray from 'streamify-array';
import { RuntimeConfig } from '../init/RuntimeConfig';
import { Representation } from '../ldp/representation/Representation'; import { Representation } from '../ldp/representation/Representation';
import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { TEXT_TURTLE } from '../util/ContentTypes'; import { TEXT_TURTLE } from '../util/ContentTypes';
@ -15,15 +14,15 @@ import { ResourceStore } from './ResourceStore';
*/ */
export class InMemoryResourceStore implements ResourceStore { export class InMemoryResourceStore implements ResourceStore {
private readonly store: { [id: string]: Representation }; private readonly store: { [id: string]: Representation };
private readonly runtimeConfig: RuntimeConfig; private readonly base: string;
private index = 0; private index = 0;
/** /**
* @param runtimeConfig - Config containing base that will be stripped of all incoming URIs * @param base - Base that will be stripped of all incoming URIs
* and added to all outgoing ones to find the relative path. * and added to all outgoing ones to find the relative path.
*/ */
public constructor(runtimeConfig: RuntimeConfig) { public constructor(base: string) {
this.runtimeConfig = runtimeConfig; this.base = ensureTrailingSlash(base);
this.store = { this.store = {
// Default root entry (what you get when the identifier is equal to the base) // Default root entry (what you get when the identifier is equal to the base)
@ -105,8 +104,8 @@ export class InMemoryResourceStore implements ResourceStore {
* @returns A string representing the relative path. * @returns A string representing the relative path.
*/ */
private parseIdentifier(identifier: ResourceIdentifier): string { private parseIdentifier(identifier: ResourceIdentifier): string {
const path = identifier.path.slice(this.runtimeConfig.base.length); const path = identifier.path.slice(this.base.length);
if (!identifier.path.startsWith(this.runtimeConfig.base)) { if (!identifier.path.startsWith(this.base)) {
throw new NotFoundHttpError(); throw new NotFoundHttpError();
} }
return path; return path;

View File

@ -1,4 +1,3 @@
import { RuntimeConfig } from '../init/RuntimeConfig';
import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier'; import { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { ensureTrailingSlash } from '../util/Util'; import { ensureTrailingSlash } from '../util/Util';
import { ContainerManager } from './ContainerManager'; import { ContainerManager } from './ContainerManager';
@ -7,15 +6,15 @@ import { ContainerManager } from './ContainerManager';
* Determines containers based on URL decomposition. * Determines containers based on URL decomposition.
*/ */
export class UrlContainerManager implements ContainerManager { export class UrlContainerManager implements ContainerManager {
private readonly runtimeConfig: RuntimeConfig; private readonly base: string;
public constructor(runtimeConfig: RuntimeConfig) { public constructor(base: string) {
this.runtimeConfig = runtimeConfig; this.base = base;
} }
public async getContainer(id: ResourceIdentifier): Promise<ResourceIdentifier> { public async getContainer(id: ResourceIdentifier): Promise<ResourceIdentifier> {
const path = this.canonicalUrl(id.path); const path = this.canonicalUrl(id.path);
if (this.runtimeConfig.base === path) { if (this.base === path) {
throw new Error('Root does not have a container.'); throw new Error('Root does not have a container.');
} }

View File

@ -8,7 +8,6 @@ import {
ResourceStore, ResourceStore,
UnsecureWebIdExtractor, UnsecureWebIdExtractor,
QuadToRdfConverter, QuadToRdfConverter,
RuntimeConfig,
} from '../../index'; } from '../../index';
import { ServerConfig } from './ServerConfig'; import { ServerConfig } from './ServerConfig';
import { import {
@ -27,13 +26,13 @@ import {
*/ */
export class AuthenticatedFileResourceStoreConfig implements ServerConfig { export class AuthenticatedFileResourceStoreConfig implements ServerConfig {
private readonly runtimeConfig: RuntimeConfig; public base: string;
public store: ResourceStore; public store: ResourceStore;
public constructor(runtimeConfig: RuntimeConfig) { public constructor(base: string, rootFilepath: string) {
this.runtimeConfig = runtimeConfig; this.base = base;
this.store = getConvertingStore( this.store = getConvertingStore(
getFileResourceStore(runtimeConfig), getFileResourceStore(base, rootFilepath),
[ new QuadToRdfConverter(), [ new QuadToRdfConverter(),
new RdfToQuadConverter() ], new RdfToQuadConverter() ],
); );
@ -50,7 +49,7 @@ export class AuthenticatedFileResourceStoreConfig implements ServerConfig {
const operationHandler = getOperationHandler(this.store); const operationHandler = getOperationHandler(this.store);
const responseWriter = new BasicResponseWriter(); const responseWriter = new BasicResponseWriter();
const authorizer = getWebAclAuthorizer(this.store, this.runtimeConfig.base); const authorizer = getWebAclAuthorizer(this.store, this.base);
const handler = new AuthenticatedLdpHandler({ const handler = new AuthenticatedLdpHandler({
requestParser, requestParser,

View File

@ -9,7 +9,6 @@ import {
RawBodyParser, RawBodyParser,
RdfToQuadConverter, RdfToQuadConverter,
ResourceStore, ResourceStore,
RuntimeConfig,
UnsecureWebIdExtractor, UnsecureWebIdExtractor,
} from '../../index'; } from '../../index';
import { ServerConfig } from './ServerConfig'; import { ServerConfig } from './ServerConfig';
@ -25,9 +24,9 @@ import { getFileResourceStore, getOperationHandler, getConvertingStore, getBasic
export class FileResourceStoreConfig implements ServerConfig { export class FileResourceStoreConfig implements ServerConfig {
public store: ResourceStore; public store: ResourceStore;
public constructor(runtimeConfig: RuntimeConfig) { public constructor(base: string, rootFilepath: string) {
this.store = getConvertingStore( this.store = getConvertingStore(
getFileResourceStore(runtimeConfig), getFileResourceStore(base, rootFilepath),
[ new QuadToRdfConverter(), new RdfToQuadConverter() ], [ new QuadToRdfConverter(), new RdfToQuadConverter() ],
); );
} }

View File

@ -23,7 +23,6 @@ import {
RepresentationConvertingStore, RepresentationConvertingStore,
ResourceStore, ResourceStore,
ResponseDescription, ResponseDescription,
RuntimeConfig,
SingleThreadedResourceLocker, SingleThreadedResourceLocker,
SparqlUpdatePatchHandler, SparqlUpdatePatchHandler,
UrlBasedAclManager, UrlBasedAclManager,
@ -32,26 +31,24 @@ import {
} from '../../index'; } from '../../index';
import { ExtensionBasedMapper } from '../../src/storage/ExtensionBasedMapper'; import { ExtensionBasedMapper } from '../../src/storage/ExtensionBasedMapper';
const BASE = 'http://test.com'; export const BASE = 'http://test.com';
/** /**
* Creates a RuntimeConfig with its rootFilePath set based on the given subfolder. * Creates a RuntimeConfig with its rootFilePath set based on the given subfolder.
* @param subfolder - Folder to use in the global testData folder. * @param subfolder - Folder to use in the global testData folder.
*/ */
export const getRuntimeConfig = (subfolder: string): RuntimeConfig => new RuntimeConfig({ export const getRootFilePath = (subfolder: string): string => join(__dirname, '../testData', subfolder);
base: BASE,
rootFilepath: join(__dirname, '../testData', subfolder),
});
/** /**
* Gives a file resource store based on (default) runtime config. * Gives a file resource store based on (default) runtime config.
* @param runtimeConfig - Optional runtime config. * @param base - Base URL.
* @param rootFilepath - The root file path.
* *
* @returns The file resource store. * @returns The file resource store.
*/ */
export const getFileResourceStore = (runtimeConfig: RuntimeConfig): FileResourceStore => export const getFileResourceStore = (base: string, rootFilepath: string): FileResourceStore =>
new FileResourceStore( new FileResourceStore(
new ExtensionBasedMapper(runtimeConfig), new ExtensionBasedMapper(base, rootFilepath),
new InteractionController(), new InteractionController(),
new MetadataController(), new MetadataController(),
); );
@ -63,7 +60,7 @@ export const getFileResourceStore = (runtimeConfig: RuntimeConfig): FileResource
* @returns The in memory resource store. * @returns The in memory resource store.
*/ */
export const getInMemoryResourceStore = (base = BASE): InMemoryResourceStore => export const getInMemoryResourceStore = (base = BASE): InMemoryResourceStore =>
new InMemoryResourceStore(new RuntimeConfig({ base })); new InMemoryResourceStore(base);
/** /**
* Gives a converting store given some converters. * Gives a converting store given some converters.
@ -138,6 +135,6 @@ export const getBasicRequestParser = (bodyParsers: BodyParser[] = []): BasicRequ
*/ */
export const getWebAclAuthorizer = export const getWebAclAuthorizer =
(store: ResourceStore, base = BASE, aclManager = new UrlBasedAclManager()): WebAclAuthorizer => { (store: ResourceStore, base = BASE, aclManager = new UrlBasedAclManager()): WebAclAuthorizer => {
const containerManager = new UrlContainerManager(new RuntimeConfig({ base })); const containerManager = new UrlContainerManager(base);
return new WebAclAuthorizer(aclManager, containerManager, store); return new WebAclAuthorizer(aclManager, containerManager, store);
}; };

View File

@ -1,9 +1,10 @@
import { copyFileSync, mkdirSync } from 'fs'; import { copyFileSync, mkdirSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import * as rimraf from 'rimraf'; import * as rimraf from 'rimraf';
import { HttpHandler, ResourceStore, RuntimeConfig } from '../../index'; import { HttpHandler, ResourceStore } from '../../index';
import { ensureTrailingSlash } from '../../src/util/Util';
import { AuthenticatedFileResourceStoreConfig } from '../configs/AuthenticatedFileResourceStoreConfig'; import { AuthenticatedFileResourceStoreConfig } from '../configs/AuthenticatedFileResourceStoreConfig';
import { getRuntimeConfig } from '../configs/Util'; import { BASE, getRootFilePath } from '../configs/Util';
import { AclTestHelper, FileTestHelper } from '../util/TestHelpers'; import { AclTestHelper, FileTestHelper } from '../util/TestHelpers';
describe('A server using a AuthenticatedFileResourceStore', (): void => { describe('A server using a AuthenticatedFileResourceStore', (): void => {
@ -12,24 +13,23 @@ describe('A server using a AuthenticatedFileResourceStore', (): void => {
let store: ResourceStore; let store: ResourceStore;
let aclHelper: AclTestHelper; let aclHelper: AclTestHelper;
let fileHelper: FileTestHelper; let fileHelper: FileTestHelper;
let runtimeConfig: RuntimeConfig; let rootFilePath: string;
beforeAll(async(): Promise<void> => { beforeAll(async(): Promise<void> => {
runtimeConfig = getRuntimeConfig('AuthenticatedFileResourceStore'); rootFilePath = getRootFilePath('AuthenticatedFileResourceStore');
config = new AuthenticatedFileResourceStoreConfig(runtimeConfig); config = new AuthenticatedFileResourceStoreConfig(BASE, rootFilePath);
const { base, rootFilepath } = runtimeConfig;
handler = config.getHttpHandler(); handler = config.getHttpHandler();
({ store } = config); ({ store } = config);
aclHelper = new AclTestHelper(store, base); aclHelper = new AclTestHelper(store, ensureTrailingSlash(BASE));
fileHelper = new FileTestHelper(handler, new URL('http://test.com/')); fileHelper = new FileTestHelper(handler, new URL(ensureTrailingSlash(BASE)));
// Make sure the root directory exists // Make sure the root directory exists
mkdirSync(rootFilepath, { recursive: true }); mkdirSync(rootFilePath, { recursive: true });
copyFileSync(join(__dirname, '../assets/permanent.txt'), `${rootFilepath}/permanent.txt`); copyFileSync(join(__dirname, '../assets/permanent.txt'), `${rootFilePath}/permanent.txt`);
}); });
afterAll(async(): Promise<void> => { afterAll(async(): Promise<void> => {
rimraf.sync(runtimeConfig.rootFilepath, { glob: false }); rimraf.sync(rootFilePath, { glob: false });
}); });
describe('with acl', (): void => { describe('with acl', (): void => {

View File

@ -1,26 +1,25 @@
import * as rimraf from 'rimraf'; import * as rimraf from 'rimraf';
import { RuntimeConfig } from '../../src/init/RuntimeConfig';
import { HttpHandler } from '../../src/server/HttpHandler'; import { HttpHandler } from '../../src/server/HttpHandler';
import { FileResourceStoreConfig } from '../configs/FileResourceStoreConfig'; import { FileResourceStoreConfig } from '../configs/FileResourceStoreConfig';
import { getRuntimeConfig } from '../configs/Util'; import { BASE, getRootFilePath } from '../configs/Util';
import { FileTestHelper } from '../util/TestHelpers'; import { FileTestHelper } from '../util/TestHelpers';
describe('A server using a FileResourceStore', (): void => { describe('A server using a FileResourceStore', (): void => {
describe('without acl', (): void => { describe('without acl', (): void => {
let rootFilePath: string;
let config: FileResourceStoreConfig; let config: FileResourceStoreConfig;
let handler: HttpHandler; let handler: HttpHandler;
let fileHelper: FileTestHelper; let fileHelper: FileTestHelper;
let runtimeConfig: RuntimeConfig;
beforeAll(async(): Promise<void> => { beforeAll(async(): Promise<void> => {
runtimeConfig = getRuntimeConfig('FileResourceStore'); rootFilePath = getRootFilePath('FileResourceStore');
config = new FileResourceStoreConfig(runtimeConfig); config = new FileResourceStoreConfig(BASE, rootFilePath);
handler = config.getHttpHandler(); handler = config.getHttpHandler();
fileHelper = new FileTestHelper(handler, new URL(runtimeConfig.base)); fileHelper = new FileTestHelper(handler, new URL(BASE));
}); });
afterAll(async(): Promise<void> => { afterAll(async(): Promise<void> => {
rimraf.sync(runtimeConfig.rootFilepath, { glob: false }); rimraf.sync(rootFilePath, { glob: false });
}); });
it('can add a file to the store, read it and delete it.', async(): it('can add a file to the store, read it and delete it.', async():

View File

@ -1,48 +0,0 @@
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
describe('RuntimeConfig', (): void => {
it('handles undefined args.', async(): Promise<void> => {
const config = new RuntimeConfig();
expect(config.port).toEqual(3000);
expect(config.base).toEqual('http://localhost:3000/');
});
it('handles empty args.', async(): Promise<void> => {
const config = new RuntimeConfig({});
expect(config.port).toEqual(3000);
expect(config.base).toEqual('http://localhost:3000/');
});
it('handles args with port.', async(): Promise<void> => {
const config = new RuntimeConfig({ port: 1234 });
expect(config.port).toEqual(1234);
expect(config.base).toEqual('http://localhost:1234/');
});
it('handles args with base.', async(): Promise<void> => {
const config = new RuntimeConfig({ base: 'http://example.org/' });
expect(config.port).toEqual(3000);
expect(config.base).toEqual('http://example.org/');
});
it('handles args with port and base.', async(): Promise<void> => {
const config = new RuntimeConfig({ port: 1234, base: 'http://example.org/' });
expect(config.port).toEqual(1234);
expect(config.base).toEqual('http://example.org/');
});
it('handles resetting data.', async(): Promise<void> => {
const config = new RuntimeConfig({});
expect(config.port).toEqual(3000);
expect(config.base).toEqual('http://localhost:3000/');
config.reset({ port: 1234, base: 'http://example.org/' });
expect(config.port).toEqual(1234);
expect(config.base).toEqual('http://example.org/');
});
it('ensures trailing slash in base.', async(): Promise<void> => {
const config = new RuntimeConfig({ base: 'http://example.org' });
expect(config.base).toEqual('http://example.org/');
});
});

View File

@ -1,4 +1,3 @@
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
import { Setup } from '../../../src/init/Setup'; import { Setup } from '../../../src/init/Setup';
describe('Setup', (): void => { describe('Setup', (): void => {
@ -16,7 +15,7 @@ describe('Setup', (): void => {
httpServer = { httpServer = {
listen: jest.fn(), listen: jest.fn(),
}; };
setup = new Setup(httpServer, store, aclManager, new RuntimeConfig()); setup = new Setup(httpServer, store, aclManager, 'http://localhost:3000/', 3000);
}); });
it('starts an HTTP server.', async(): Promise<void> => { it('starts an HTTP server.', async(): Promise<void> => {

View File

@ -1,10 +1,9 @@
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper'; import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper';
describe('An ExtensionBasedMapper', (): void => { describe('An ExtensionBasedMapper', (): void => {
const base = 'http://test.com/'; const base = 'http://test.com/';
const rootFilepath = 'uploads/'; const rootFilepath = 'uploads/';
const resourceMapper = new ExtensionBasedMapper(new RuntimeConfig({ base, rootFilepath })); const resourceMapper = new ExtensionBasedMapper(base, rootFilepath);
it('returns the correct url of a file.', async(): Promise<void> => { it('returns the correct url of a file.', async(): Promise<void> => {
let result = resourceMapper.mapFilePathToUrl(`${rootFilepath}test.txt`); let result = resourceMapper.mapFilePathToUrl(`${rootFilepath}test.txt`);

View File

@ -5,7 +5,6 @@ import { literal, namedNode, quad as quadRDF, triple } from '@rdfjs/data-model';
import arrayifyStream from 'arrayify-stream'; import arrayifyStream from 'arrayify-stream';
import { DataFactory } from 'n3'; import { DataFactory } from 'n3';
import streamifyArray from 'streamify-array'; import streamifyArray from 'streamify-array';
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
import { Representation } from '../../../src/ldp/representation/Representation'; import { Representation } from '../../../src/ldp/representation/Representation';
import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata'; import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper'; import { ExtensionBasedMapper } from '../../../src/storage/ExtensionBasedMapper';
@ -55,7 +54,7 @@ describe('A FileResourceStore', (): void => {
jest.clearAllMocks(); jest.clearAllMocks();
store = new FileResourceStore( store = new FileResourceStore(
new ExtensionBasedMapper(new RuntimeConfig({ base, rootFilepath })), new ExtensionBasedMapper(base, rootFilepath),
new InteractionController(), new InteractionController(),
new MetadataController(), new MetadataController(),
); );

View File

@ -1,6 +1,5 @@
import { Readable } from 'stream'; import { Readable } from 'stream';
import streamifyArray from 'streamify-array'; import streamifyArray from 'streamify-array';
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
import { Representation } from '../../../src/ldp/representation/Representation'; import { Representation } from '../../../src/ldp/representation/Representation';
import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata'; import { RepresentationMetadata } from '../../../src/ldp/representation/RepresentationMetadata';
import { InMemoryResourceStore } from '../../../src/storage/InMemoryResourceStore'; import { InMemoryResourceStore } from '../../../src/storage/InMemoryResourceStore';
@ -15,7 +14,7 @@ describe('A InMemoryResourceStore', (): void => {
const dataString = '<http://test.com/s> <http://test.com/p> <http://test.com/o>.'; const dataString = '<http://test.com/s> <http://test.com/p> <http://test.com/o>.';
beforeEach(async(): Promise<void> => { beforeEach(async(): Promise<void> => {
store = new InMemoryResourceStore(new RuntimeConfig({ base })); store = new InMemoryResourceStore(base);
representation = { representation = {
binary: true, binary: true,

View File

@ -1,9 +1,8 @@
import { RuntimeConfig } from '../../../src/init/RuntimeConfig';
import { UrlContainerManager } from '../../../src/storage/UrlContainerManager'; import { UrlContainerManager } from '../../../src/storage/UrlContainerManager';
describe('An UrlContainerManager', (): void => { describe('An UrlContainerManager', (): void => {
it('returns the parent URl for a single call.', async(): Promise<void> => { it('returns the parent URl for a single call.', async(): Promise<void> => {
const manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' })); const manager = new UrlContainerManager('http://test.com/foo/');
await expect(manager.getContainer({ path: 'http://test.com/foo/bar' })) await expect(manager.getContainer({ path: 'http://test.com/foo/bar' }))
.resolves.toEqual({ path: 'http://test.com/foo/' }); .resolves.toEqual({ path: 'http://test.com/foo/' });
await expect(manager.getContainer({ path: 'http://test.com/foo/bar/' })) await expect(manager.getContainer({ path: 'http://test.com/foo/bar/' }))
@ -11,13 +10,13 @@ describe('An UrlContainerManager', (): void => {
}); });
it('errors when getting the container of root.', async(): Promise<void> => { it('errors when getting the container of root.', async(): Promise<void> => {
let manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' })); let manager = new UrlContainerManager('http://test.com/foo/');
await expect(manager.getContainer({ path: 'http://test.com/foo/' })) await expect(manager.getContainer({ path: 'http://test.com/foo/' }))
.rejects.toThrow('Root does not have a container.'); .rejects.toThrow('Root does not have a container.');
await expect(manager.getContainer({ path: 'http://test.com/foo' })) await expect(manager.getContainer({ path: 'http://test.com/foo' }))
.rejects.toThrow('Root does not have a container.'); .rejects.toThrow('Root does not have a container.');
manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo' })); manager = new UrlContainerManager('http://test.com/foo/');
await expect(manager.getContainer({ path: 'http://test.com/foo/' })) await expect(manager.getContainer({ path: 'http://test.com/foo/' }))
.rejects.toThrow('Root does not have a container.'); .rejects.toThrow('Root does not have a container.');
await expect(manager.getContainer({ path: 'http://test.com/foo' })) await expect(manager.getContainer({ path: 'http://test.com/foo' }))
@ -25,7 +24,7 @@ describe('An UrlContainerManager', (): void => {
}); });
it('errors when the root of an URl is reached that does not match the input root.', async(): Promise<void> => { it('errors when the root of an URl is reached that does not match the input root.', async(): Promise<void> => {
const manager = new UrlContainerManager(new RuntimeConfig({ base: 'http://test.com/foo/' })); const manager = new UrlContainerManager('http://test.com/foo/');
await expect(manager.getContainer({ path: 'http://test.com/' })) await expect(manager.getContainer({ path: 'http://test.com/' }))
.rejects.toThrow('URL root reached.'); .rejects.toThrow('URL root reached.');
}); });