feat: Enable dependency injection with auto-generated components

This commit is contained in:
Ruben Taelman 2020-08-25 10:24:53 +02:00 committed by Joachim Van Herwegen
parent e88e680ed7
commit db04c55196
13 changed files with 3265 additions and 1836 deletions

2
.gitignore vendored
View File

@ -9,3 +9,5 @@ coverage
!.eslintrc.js
!test/eslintrc.js
!jest.config.js
components

View File

@ -1,107 +1,4 @@
#!/usr/bin/env node
import yargs from 'yargs';
import {
AcceptPreferenceParser,
AuthenticatedLdpHandler,
BasePermissionsExtractor,
CompositeAsyncHandler,
ExpressHttpServer,
RdfToQuadConverter,
HttpRequest,
PatchingStore,
QuadToTurtleConverter,
Representation,
RepresentationConvertingStore,
RuntimeConfig,
Setup,
SimpleAclAuthorizer,
SimpleBodyParser,
SimpleCredentialsExtractor,
SimpleDeleteOperationHandler,
SimpleExtensionAclManager,
SimpleGetOperationHandler,
SimplePatchOperationHandler,
SimplePostOperationHandler,
SimplePutOperationHandler,
SimpleRequestParser,
SimpleResourceStore,
SimpleResponseWriter,
SimpleSparqlUpdateBodyParser,
SimpleSparqlUpdatePatchHandler,
SimpleTargetExtractor,
SingleThreadedResourceLocker,
SparqlPatchPermissionsExtractor,
UrlContainerManager,
} from '..';
const { argv } = yargs
.usage('node ./bin/server.js [args]')
.options({
port: { type: 'number', alias: 'p' },
})
.help();
// This is instead of the dependency injection that still needs to be added
const runtimeConfig = new RuntimeConfig();
const bodyParser = new CompositeAsyncHandler<HttpRequest, Representation | undefined>([
new SimpleSparqlUpdateBodyParser(),
new SimpleBodyParser(),
]);
const requestParser = new SimpleRequestParser({
targetExtractor: new SimpleTargetExtractor(),
preferenceParser: new AcceptPreferenceParser(),
bodyParser,
});
const credentialsExtractor = new SimpleCredentialsExtractor();
const permissionsExtractor = new CompositeAsyncHandler([
new BasePermissionsExtractor(),
new SparqlPatchPermissionsExtractor(),
]);
// Will have to see how to best handle this
const store = new SimpleResourceStore(runtimeConfig);
const converter = new CompositeAsyncHandler([
new RdfToQuadConverter(),
new QuadToTurtleConverter(),
]);
const convertingStore = new RepresentationConvertingStore(store, converter);
const locker = new SingleThreadedResourceLocker();
const patcher = new SimpleSparqlUpdatePatchHandler(convertingStore, locker);
const patchingStore = new PatchingStore(convertingStore, patcher);
const aclManager = new SimpleExtensionAclManager();
const containerManager = new UrlContainerManager(runtimeConfig);
const authorizer = new SimpleAclAuthorizer(aclManager, containerManager, patchingStore);
const operationHandler = new CompositeAsyncHandler([
new SimpleDeleteOperationHandler(patchingStore),
new SimpleGetOperationHandler(patchingStore),
new SimplePatchOperationHandler(patchingStore),
new SimplePostOperationHandler(patchingStore),
new SimplePutOperationHandler(patchingStore),
]);
const responseWriter = new SimpleResponseWriter();
const httpHandler = new AuthenticatedLdpHandler({
requestParser,
credentialsExtractor,
permissionsExtractor,
authorizer,
operationHandler,
responseWriter,
});
const httpServer = new ExpressHttpServer(httpHandler);
const setup = new Setup(httpServer, store, aclManager, runtimeConfig);
runtimeConfig.reset({ port: argv.port });
setup.setup().then((): void => {
process.stdout.write(`Running at ${runtimeConfig.base}\n`);
}).catch((error): void => {
process.stderr.write(`${error}\n`);
process.exit(1);
});
import * as Path from 'path';
import { runCli } from '../src/init/CliRunner';
runCli(Path.join(__dirname, '..'));

View File

@ -0,0 +1,10 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"import": [
"files-scs:config/presets/acl.json",
"files-scs:config/presets/http.json",
"files-scs:config/presets/ldp.json",
"files-scs:config/presets/setup.json",
"files-scs:config/presets/storage.json"
]
}

9
config/presets/acl.json Normal file
View File

@ -0,0 +1,9 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:my:AclManager",
"@type": "SimpleExtensionAclManager"
}
]
}

12
config/presets/http.json Normal file
View File

@ -0,0 +1,12 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:my:HttpServer",
"@type": "ExpressHttpServer",
"ExpressHttpServer:_handler": {
"@id": "urn:solid-server:my:HttpHandler"
}
}
]
}

99
config/presets/ldp.json Normal file
View File

@ -0,0 +1,99 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:my:HttpHandler",
"@type": "AuthenticatedLdpHandler",
"AuthenticatedLdpHandler:_requestParser": {
"@type": "SimpleRequestParser",
"SimpleRequestParser:_targetExtractor": {
"@type": "SimpleTargetExtractor"
},
"SimpleRequestParser:_preferenceParser": {
"@type": "AcceptPreferenceParser"
},
"SimpleRequestParser:_bodyParser": {
"@type": "CompositeAsyncHandler",
"CompositeAsyncHandler:_handlers": [
{
"@type": "SimpleSparqlUpdateBodyParser"
},
{
"@type": "SimpleBodyParser"
}
]
}
},
"AuthenticatedLdpHandler:_credentialsExtractor": {
"@type": "SimpleCredentialsExtractor"
},
"AuthenticatedLdpHandler:_permissionsExtractor": {
"@type": "CompositeAsyncHandler",
"CompositeAsyncHandler:_handlers": [
{
"@type": "BasePermissionsExtractor"
},
{
"@type": "SparqlPatchPermissionsExtractor"
}
]
},
"AuthenticatedLdpHandler:_authorizer": {
"@type": "SimpleAclAuthorizer",
"SimpleAclAuthorizer:_aclManager": {
"@id": "urn:solid-server:my:AclManager"
},
"SimpleAclAuthorizer:_containerManager": {
"@id": "urn:solid-server:my:UrlContainerManager"
},
"SimpleAclAuthorizer:_resourceStore": {
"@id": "urn:solid-server:my:ResourceStore"
}
},
"AuthenticatedLdpHandler:_operationHandler": {
"@type": "CompositeAsyncHandler",
"CompositeAsyncHandler:_handlers": [
{
"@type": "SimpleDeleteOperationHandler",
"SimpleDeleteOperationHandler:_store": {
"@id": "urn:solid-server:my:ResourceStore"
}
},
{
"@type": "SimpleGetOperationHandler",
"SimpleGetOperationHandler:_store": {
"@id": "urn:solid-server:my:ResourceStore"
}
},
{
"@type": "SimplePatchOperationHandler",
"SimplePatchOperationHandler:_store": {
"@id": "urn:solid-server:my:ResourceStore"
}
},
{
"@type": "SimplePostOperationHandler",
"SimplePostOperationHandler:_store": {
"@id": "urn:solid-server:my:ResourceStore"
}
},
{
"@type": "SimplePutOperationHandler",
"SimplePutOperationHandler:_store": {
"@id": "urn:solid-server:my:ResourceStore"
}
}
]
},
"AuthenticatedLdpHandler:_responseWriter": {
"@type": "SimpleResponseWriter"
}
}
]
}

23
config/presets/setup.json Normal file
View File

@ -0,0 +1,23 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:my",
"@type": "Setup",
"Setup:_httpServer": {
"@id": "urn:solid-server:my:HttpServer"
},
"Setup:_store": {
"@id": "urn:solid-server:my:ResourceStore"
},
"Setup:_aclManager": {
"@id": "urn:solid-server:my:AclManager"
},
"Setup:_runtimeConfig": {
"@id": "urn:solid-server:my:RuntimeConfig",
"@type": "RuntimeConfig",
"RuntimeConfigData:_port": 3000
}
}
]
}

View File

@ -0,0 +1,57 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
"@graph": [
{
"@id": "urn:solid-server:my:ResourceLocker",
"@type": "SingleThreadedResourceLocker"
},
{
"@id": "urn:solid-server:my:ResourceStore",
"@type": "PatchingStore",
"PatchingStore:_source": {
"@id": "urn:solid-server:my:ResourceStore_Converting"
},
"PatchingStore:_patcher": {
"@id": "urn:solid-server:my:PatchHandler",
"@type": "SimpleSparqlUpdatePatchHandler",
"SimpleSparqlUpdatePatchHandler:_source": {
"@id": "urn:solid-server:my:ResourceStore_Converting"
},
"SimpleSparqlUpdatePatchHandler:_locker": {
"@id": "urn:solid-server:my:ResourceLocker"
}
}
},
{
"@id": "urn:solid-server:my:ResourceStore_Converting",
"@type": "RepresentationConvertingStore",
"RepresentationConvertingStore:_source": {
"@type": "SimpleResourceStore",
"SimpleResourceStore:_runtimeConfig": {
"@id": "urn:solid-server:my:RuntimeConfig"
}
},
"RepresentationConvertingStore:_converter": {
"@type": "CompositeAsyncHandler",
"CompositeAsyncHandler:_handlers": [
{
"@type": "RdfToQuadConverter"
},
{
"@type": "QuadToTurtleConverter"
}
]
}
},
{
"@id": "urn:solid-server:my:UrlContainerManager",
"@type": "UrlContainerManager",
"UrlContainerManager:_runtimeConfig": {
"@id": "urn:solid-server:my:RuntimeConfig"
}
}
]
}

View File

@ -11,6 +11,7 @@ export * from './src/authorization/SimpleAuthorizer';
export * from './src/authorization/SimpleExtensionAclManager';
// Init
export * from './src/init/CliRunner';
export * from './src/init/RuntimeConfig';
export * from './src/init/Setup';

4698
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,22 @@
{
"name": "@solid/community-server",
"version": "0.0.1",
"lsd:module": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server",
"lsd:components": "components/components.jsonld",
"lsd:contexts": {
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld": "components/context.jsonld"
},
"lsd:importPaths": {
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/": "components/",
"https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/config/": "config/"
},
"main": "index.js",
"license": "MIT",
"bin": "bin/server.js",
"scripts": {
"build": "tsc",
"build": "npm run build:ts && npm run build:components",
"build:ts": "tsc",
"build:components": "componentsjs-generator -s src",
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls",
"lint": "eslint . --ext .ts",
"prepare": "npm run build",
@ -24,7 +35,9 @@
"bin/*.js",
"bin/*.d.ts",
"src/**/*.js",
"src/**/*.d.ts"
"src/**/*.d.ts",
"components/**/*.jsonld",
"config/**/*.json"
],
"dependencies": {
"@rdfjs/data-model": "^1.1.2",
@ -38,6 +51,7 @@
"@types/uuid": "^8.3.0",
"@types/yargs": "^15.0.5",
"async-lock": "^1.2.4",
"componentsjs": "^3.4.2",
"cors": "^2.8.5",
"express": "^4.17.1",
"mime-types": "^2.1.27",
@ -56,6 +70,7 @@
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"arrayify-stream": "^1.0.0",
"componentjs-generator": "^1.0.0",
"coveralls": "^3.1.0",
"eslint": "^7.0.0",
"eslint-config-es": "^3.19.61",

50
src/init/CliRunner.ts Normal file
View File

@ -0,0 +1,50 @@
import { ReadStream, WriteStream } from 'tty';
import { Loader, LoaderProperties } from 'componentsjs';
import yargs from 'yargs';
import { RuntimeConfig } from './RuntimeConfig';
import { Setup } from './Setup';
/**
* Generic run function for starting the server from a given config
* @param args - Command line arguments.
* @param stdin - Standard input stream.
* @param stdout - Standard output stream.
* @param stderr - Standard error stream.
* @param properties - Components loader properties.
*/
export const runCustom = function(
args: string[],
stdin: ReadStream,
stdout: WriteStream,
stderr: WriteStream,
properties: LoaderProperties,
): void {
const { argv } = yargs
.usage('node ./bin/server.js [args]')
.options({
port: { type: 'number', alias: 'p' },
})
.help();
new Promise<RuntimeConfig>(async(resolve): Promise<void> => {
// Setup from config file
const loader = new Loader(properties);
await loader.registerAvailableModuleResources();
const setup: Setup = await loader
.instantiateFromUrl('urn:solid-server:my', `${__dirname}/../../config/config-default.json`);
resolve(await setup.setup({ port: argv.port }));
}).then((runtimeConfig: RuntimeConfig): void => {
stdout.write(`Running at ${runtimeConfig.base}\n`);
}).catch((error): void => {
stderr.write(`${error}\n`);
});
};
/**
* Run function for starting the server from the command line
* @param moduleRootPath - Path to the module's root.
*/
export const runCli = function(moduleRootPath: string): void {
const argv = process.argv.slice(2);
runCustom(argv, process.stdin, process.stdout, process.stderr, { mainModulePath: moduleRootPath });
};

View File

@ -3,7 +3,7 @@ import { AclManager } from '../authorization/AclManager';
import { ExpressHttpServer } from '../server/ExpressHttpServer';
import { ResourceStore } from '../storage/ResourceStore';
import { DATA_TYPE_BINARY } from '../util/ContentTypes';
import { RuntimeConfig } from './RuntimeConfig';
import { RuntimeConfig, RuntimeConfigData } from './RuntimeConfig';
/**
* Invokes all logic to setup a server.
@ -28,10 +28,11 @@ export class Setup {
/**
* Set up a server at the given port and base URL.
* @param port - A port number.
* @param base - A base URL.
* @param data - Runtime config data.
*/
public async setup(): Promise<void> {
public async setup(data: RuntimeConfigData = {}): Promise<RuntimeConfig> {
this.runtimeConfig.reset(data);
// 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
const aclSetup = async(): Promise<void> => {
@ -61,9 +62,10 @@ export class Setup {
},
);
};
await aclSetup();
this.httpServer.listen(this.runtimeConfig.port);
return this.runtimeConfig;
}
}