mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Accept both Settings and VariableBindings to create an App
This commit is contained in:
parent
8609704528
commit
f609f1a9c5
@ -19,7 +19,7 @@
|
|||||||
"Promise",
|
"Promise",
|
||||||
"Readonly",
|
"Readonly",
|
||||||
"RegExp",
|
"RegExp",
|
||||||
"Settings",
|
"Shorthand",
|
||||||
"Template",
|
"Template",
|
||||||
"TemplateEngine",
|
"TemplateEngine",
|
||||||
"ValuePreferencesArg",
|
"ValuePreferencesArg",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
you should also upgrade to prevent warnings and conflicts.
|
you should also upgrade to prevent warnings and conflicts.
|
||||||
- A new FileSystemResourceLocker has been added. It allows for true threadsafe locking without external dependencies.
|
- A new FileSystemResourceLocker has been added. It allows for true threadsafe locking without external dependencies.
|
||||||
- The CSS can now run multithreaded with multiple workers, this is done with the `--workers` or `-w` flag.
|
- The CSS can now run multithreaded with multiple workers, this is done with the `--workers` or `-w` flag.
|
||||||
|
- When starting the server through code, it is now possible to provide CLI value bindings as well in `AppRunner`.
|
||||||
|
|
||||||
### Data migration
|
### Data migration
|
||||||
The following actions are required if you are upgrading from a v4 server and want to retain your data.
|
The following actions are required if you are upgrading from a v4 server and want to retain your data.
|
||||||
@ -25,7 +26,7 @@ The following changes pertain to the imports in the default configs:
|
|||||||
making them threadsafe.
|
making them threadsafe.
|
||||||
|
|
||||||
The following changes are relevant for v4 custom configs that replaced certain features.
|
The following changes are relevant for v4 custom configs that replaced certain features.
|
||||||
- `config/app/variables/cli.json` was changed to support the new `YargsCliExtractor` format.
|
- `config/app/variables/*` was changed to support the new `YargsCliExtractor` format and `SettingsResolver` rename.
|
||||||
- `config/util/resource-locker/memory.json` had the locker @type changed from `SingleThreadedResourceLocker` to `MemoryResourceLocker`.
|
- `config/util/resource-locker/memory.json` had the locker @type changed from `SingleThreadedResourceLocker` to `MemoryResourceLocker`.
|
||||||
- The content-length parser has been moved from the default configuration to the quota configurations.
|
- The content-length parser has been moved from the default configuration to the quota configurations.
|
||||||
- `/ldp/metadata-parser/default.json`
|
- `/ldp/metadata-parser/default.json`
|
||||||
@ -73,6 +74,7 @@ These changes are relevant if you wrote custom modules for the server that depen
|
|||||||
- `ResourceStore` functions that change a resource now return metadata for every changed resource.
|
- `ResourceStore` functions that change a resource now return metadata for every changed resource.
|
||||||
- All permission related interfaces have changed to support permissions over multiple identifiers.
|
- All permission related interfaces have changed to support permissions over multiple identifiers.
|
||||||
- `IdentifierStrategy` has a new `contains` method.
|
- `IdentifierStrategy` has a new `contains` method.
|
||||||
|
- `SettingsResolver` was renamed to `ShorthandResolver`, together with all related classes and parameters.
|
||||||
|
|
||||||
A new interface `SingleThreaded` has been added. This empty interface can be implemented to mark a component as not-threadsafe. When the CSS starts in multithreaded mode, it will error and halt if any SingleThreaded components are instantiated.
|
A new interface `SingleThreaded` has been added. This empty interface can be implemented to mark a component as not-threadsafe. When the CSS starts in multithreaded mode, it will error and halt if any SingleThreaded components are instantiated.
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^5.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^5.0.0/components/context.jsonld",
|
||||||
"import": [
|
"import": [
|
||||||
"css:config/app/variables/cli/cli.json",
|
"css:config/app/variables/cli/cli.json",
|
||||||
"css:config/app/variables/resolver/resolver.json"
|
"css:config/app/variables/resolver/resolver.json"
|
||||||
],
|
],
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"comment": "Combines a CliExtractor and SettingsResolver to be used by the AppRunner.",
|
"comment": "Combines a CliExtractor and ShorthandResolver to be used by the AppRunner.",
|
||||||
"@id": "urn:solid-server-app-setup:default:CliResolver",
|
"@id": "urn:solid-server-app-setup:default:CliResolver",
|
||||||
"@type": "CliResolver",
|
"@type": "CliResolver",
|
||||||
"cliExtractor": { "@id": "urn:solid-server-app-setup:default:CliExtractor" },
|
"cliExtractor": { "@id": "urn:solid-server-app-setup:default:CliExtractor" },
|
||||||
"settingsResolver": { "@id": "urn:solid-server-app-setup:default:SettingsResolver" }
|
"shorthandResolver": { "@id": "urn:solid-server-app-setup:default:ShorthandResolver" }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -3,72 +3,72 @@
|
|||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"comment": "Converts an input key/value object into an object mapping values to Components.js variables",
|
"comment": "Converts an input key/value object into an object mapping values to Components.js variables",
|
||||||
"@id": "urn:solid-server-app-setup:default:SettingsResolver",
|
"@id": "urn:solid-server-app-setup:default:ShorthandResolver",
|
||||||
"@type": "CombinedSettingsResolver",
|
"@type": "CombinedShorthandResolver",
|
||||||
"resolvers": [
|
"resolvers": [
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:baseUrl",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:baseUrl",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "BaseUrlExtractor"
|
"@type": "BaseUrlExtractor"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:loggingLevel",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:loggingLevel",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "loggingLevel",
|
"key": "loggingLevel",
|
||||||
"defaultValue": "info"
|
"defaultValue": "info"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:port",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:port",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "port",
|
"key": "port",
|
||||||
"defaultValue": 3000
|
"defaultValue": 3000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:rootFilePath",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:rootFilePath",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "AssetPathExtractor",
|
"@type": "AssetPathExtractor",
|
||||||
"key": "rootFilePath",
|
"key": "rootFilePath",
|
||||||
"defaultPath": "./"
|
"defaultPath": "./"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:sparqlEndpoint",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:sparqlEndpoint",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "sparqlEndpoint"
|
"key": "sparqlEndpoint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:showStackTrace",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:showStackTrace",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "showStackTrace",
|
"key": "showStackTrace",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:podConfigJson",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:podConfigJson",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "AssetPathExtractor",
|
"@type": "AssetPathExtractor",
|
||||||
"key": "podConfigJson",
|
"key": "podConfigJson",
|
||||||
"defaultPath": "./pod-config.json"
|
"defaultPath": "./pod-config.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:seededPodConfigJson",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:seededPodConfigJson",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "AssetPathExtractor",
|
"@type": "AssetPathExtractor",
|
||||||
"key": "seededPodConfigJson"
|
"key": "seededPodConfigJson"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:workers",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:default:variable:workers",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "workers",
|
"key": "workers",
|
||||||
"defaultValue": 1
|
"defaultValue": 1
|
||||||
|
@ -67,19 +67,19 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Adds resolvers to assign the CLI values to the Components.js variables.",
|
"comment": "Adds resolvers to assign the CLI values to the Components.js variables.",
|
||||||
"@id": "urn:solid-server-app-setup:default:SettingsResolver",
|
"@id": "urn:solid-server-app-setup:default:ShorthandResolver",
|
||||||
"@type": "CombinedSettingsResolver",
|
"@type": "CombinedShorthandResolver",
|
||||||
"resolvers": [
|
"resolvers": [
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsKey",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsKey",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "httpsKey"
|
"key": "httpsKey"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsCert",
|
"CombinedShorthandResolver:_resolvers_key": "urn:solid-server:custom:variable:httpsCert",
|
||||||
"CombinedSettingsResolver:_resolvers_value": {
|
"CombinedShorthandResolver:_resolvers_value": {
|
||||||
"@type": "KeyExtractor",
|
"@type": "KeyExtractor",
|
||||||
"key": "httpsCert"
|
"key": "httpsCert"
|
||||||
}
|
}
|
||||||
|
@ -214,11 +214,11 @@ export * from './init/cli/YargsCliExtractor';
|
|||||||
export * from './init/variables/extractors/KeyExtractor';
|
export * from './init/variables/extractors/KeyExtractor';
|
||||||
export * from './init/variables/extractors/AssetPathExtractor';
|
export * from './init/variables/extractors/AssetPathExtractor';
|
||||||
export * from './init/variables/extractors/BaseUrlExtractor';
|
export * from './init/variables/extractors/BaseUrlExtractor';
|
||||||
export * from './init/variables/extractors/SettingsExtractor';
|
export * from './init/variables/extractors/ShorthandExtractor';
|
||||||
|
|
||||||
// Init/Variables
|
// Init/Variables
|
||||||
export * from './init/variables/CombinedSettingsResolver';
|
export * from './init/variables/CombinedShorthandResolver';
|
||||||
export * from './init/variables/SettingsResolver';
|
export * from './init/variables/ShorthandResolver';
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
export * from './init/App';
|
export * from './init/App';
|
||||||
|
@ -9,9 +9,11 @@ import { createErrorMessage, isError } from '../util/errors/ErrorUtil';
|
|||||||
import { InternalServerError } from '../util/errors/InternalServerError';
|
import { InternalServerError } from '../util/errors/InternalServerError';
|
||||||
import { resolveModulePath, resolveAssetPath } from '../util/PathUtil';
|
import { resolveModulePath, resolveAssetPath } from '../util/PathUtil';
|
||||||
import type { App } from './App';
|
import type { App } from './App';
|
||||||
|
import type { CliExtractor } from './cli/CliExtractor';
|
||||||
import type { CliResolver } from './CliResolver';
|
import type { CliResolver } from './CliResolver';
|
||||||
import { listSingleThreadedComponents } from './cluster/SingleThreaded';
|
import { listSingleThreadedComponents } from './cluster/SingleThreaded';
|
||||||
import type { CliArgv, VariableBindings } from './variables/Types';
|
import type { ShorthandResolver } from './variables/ShorthandResolver';
|
||||||
|
import type { CliArgv, Shorthand, VariableBindings } from './variables/Types';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = resolveModulePath('config/default.json');
|
const DEFAULT_CONFIG = resolveModulePath('config/default.json');
|
||||||
|
|
||||||
@ -33,41 +35,62 @@ export class AppRunner {
|
|||||||
/**
|
/**
|
||||||
* Starts the server with a given config.
|
* Starts the server with a given config.
|
||||||
* This method can be used to start the server from within another JavaScript application.
|
* This method can be used to start the server from within another JavaScript application.
|
||||||
|
*
|
||||||
* Keys of the `variableBindings` object should be Components.js variables.
|
* Keys of the `variableBindings` object should be Components.js variables.
|
||||||
* E.g.: `{ 'urn:solid-server:default:variable:rootFilePath': '.data' }`.
|
* E.g.: `{ 'urn:solid-server:default:variable:rootFilePath': '.data' }`.
|
||||||
*
|
*
|
||||||
|
* `shorthand` are CLI argument names and their corresponding values.
|
||||||
|
* E.g.: `{ rootFilePath: '.data' }`.
|
||||||
|
* Abbreviated parameter names can not be used, so `{ f: '.data' }` would not work.
|
||||||
|
*
|
||||||
|
* The values in `variableBindings` take priority over those in `shorthand`.
|
||||||
|
*
|
||||||
* @param loaderProperties - Components.js loader properties.
|
* @param loaderProperties - Components.js loader properties.
|
||||||
* @param configFile - Path to the server config file.
|
* @param configFile - Path to the server config file.
|
||||||
* @param variableBindings - Parameters to pass into the VariableResolver.
|
* @param variableBindings - Bindings of Components.js variables.
|
||||||
|
* @param shorthand - Shorthand values that need to be resolved.
|
||||||
*/
|
*/
|
||||||
public async run(
|
public async run(
|
||||||
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
||||||
configFile: string,
|
configFile: string,
|
||||||
variableBindings: VariableBindings,
|
variableBindings?: VariableBindings,
|
||||||
|
shorthand?: Shorthand,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const app = await this.create(loaderProperties, configFile, variableBindings);
|
const app = await this.create(loaderProperties, configFile, variableBindings, shorthand);
|
||||||
await app.start();
|
await app.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an App object, created with the given config, that can start and stop the Solid server.
|
* Returns an App object, created with the given config, that can start and stop the Solid server.
|
||||||
|
*
|
||||||
* Keys of the `variableBindings` object should be Components.js variables.
|
* Keys of the `variableBindings` object should be Components.js variables.
|
||||||
* E.g.: `{ 'urn:solid-server:default:variable:rootFilePath': '.data' }`.
|
* E.g.: `{ 'urn:solid-server:default:variable:rootFilePath': '.data' }`.
|
||||||
*
|
*
|
||||||
|
* `shorthand` are CLI argument names and their corresponding values.
|
||||||
|
* E.g.: `{ rootFilePath: '.data' }`.
|
||||||
|
* Abbreviated parameter names can not be used, so `{ f: '.data' }` would not work.
|
||||||
|
*
|
||||||
|
* The values in `variableBindings` take priority over those in `shorthand`.
|
||||||
|
*
|
||||||
* @param loaderProperties - Components.js loader properties.
|
* @param loaderProperties - Components.js loader properties.
|
||||||
* @param configFile - Path to the server config file.
|
* @param configFile - Path to the server config file.
|
||||||
* @param variableBindings - Bindings of Components.js variables.
|
* @param variableBindings - Bindings of Components.js variables.
|
||||||
|
* @param shorthand - Shorthand values that need to be resolved.
|
||||||
*/
|
*/
|
||||||
public async create(
|
public async create(
|
||||||
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
loaderProperties: IComponentsManagerBuilderOptions<App>,
|
||||||
configFile: string,
|
configFile: string,
|
||||||
variableBindings: VariableBindings,
|
variableBindings?: VariableBindings,
|
||||||
|
shorthand?: Shorthand,
|
||||||
): Promise<App> {
|
): Promise<App> {
|
||||||
// Create a resolver to translate (non-core) CLI parameters into values for variables
|
const componentsManager = await this.createComponentsManager<any>(loaderProperties, configFile);
|
||||||
const componentsManager = await this.createComponentsManager<App>(loaderProperties, configFile);
|
|
||||||
|
|
||||||
// Create the application using the translated variable values
|
const cliResolver = await this.createCliResolver(componentsManager);
|
||||||
return await this.createApp(componentsManager, variableBindings);
|
const parsedVariables = await this.resolveShorthand(cliResolver.shorthandResolver, { ...shorthand });
|
||||||
|
|
||||||
|
// Create the application using the translated variable values.
|
||||||
|
// `variableBindings` override those resolved from the `shorthand` input.
|
||||||
|
return this.createApp(componentsManager, { ...parsedVariables, ...variableBindings });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +161,7 @@ export class AppRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the CLI components and use them to generate values for the Components.js variables
|
// Build the CLI components and use them to generate values for the Components.js variables
|
||||||
const variables = await this.resolveVariables(componentsManager, argv);
|
const variables = await this.cliToVariables(componentsManager, argv);
|
||||||
|
|
||||||
// Build and start the actual server application using the generated variable values
|
// Build and start the actual server application using the generated variable values
|
||||||
return await this.createApp(componentsManager, variables);
|
return await this.createApp(componentsManager, variables);
|
||||||
@ -157,20 +180,50 @@ export class AppRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the first Components.js instantiation,
|
* Handles the first Components.js instantiation.
|
||||||
* where CLI settings and variable mappings are created.
|
* Uses it to extract the CLI shorthand values and use those to create variable bindings.
|
||||||
*/
|
*/
|
||||||
private async resolveVariables(componentsManager: ComponentsManager<CliResolver>, argv: string[]):
|
private async cliToVariables(componentsManager: ComponentsManager<CliResolver>, argv: CliArgv):
|
||||||
Promise<VariableBindings> {
|
Promise<VariableBindings> {
|
||||||
|
const cliResolver = await this.createCliResolver(componentsManager);
|
||||||
|
const shorthand = await this.extractShorthand(cliResolver.cliExtractor, argv);
|
||||||
|
return await this.resolveShorthand(cliResolver.shorthandResolver, shorthand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates the {@link CliResolver}.
|
||||||
|
*/
|
||||||
|
private async createCliResolver(componentsManager: ComponentsManager<CliResolver>): Promise<CliResolver> {
|
||||||
try {
|
try {
|
||||||
// Create a CliResolver, which combines a CliExtractor and a VariableResolver
|
// Create a CliResolver, which combines a CliExtractor and a VariableResolver
|
||||||
const resolver = await componentsManager.instantiate(DEFAULT_CLI_RESOLVER, {});
|
return await componentsManager.instantiate(DEFAULT_CLI_RESOLVER, {});
|
||||||
// Convert CLI args to CLI bindings
|
|
||||||
const cliValues = await resolver.cliExtractor.handleSafe(argv);
|
|
||||||
// Convert CLI bindings into variable bindings
|
|
||||||
return await resolver.settingsResolver.handleSafe(cliValues);
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
this.resolveError(`Could not load the config variables`, error);
|
this.resolveError(`Could not create the CLI resolver`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the {@link CliExtractor} to convert the CLI args to a {@link Shorthand} object.
|
||||||
|
*/
|
||||||
|
private async extractShorthand(cliExtractor: CliExtractor, argv: CliArgv): Promise<Shorthand> {
|
||||||
|
try {
|
||||||
|
// Convert CLI args to CLI bindings
|
||||||
|
return await cliExtractor.handleSafe(argv);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.resolveError(`Could not parse the CLI parameters`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the {@link ShorthandResolver} to convert {@link Shorthand} to {@link VariableBindings} .
|
||||||
|
*/
|
||||||
|
private async resolveShorthand(shorthandResolver: ShorthandResolver, shorthand: Shorthand):
|
||||||
|
Promise<VariableBindings> {
|
||||||
|
try {
|
||||||
|
// Convert CLI bindings into variable bindings
|
||||||
|
return await shorthandResolver.handleSafe(shorthand);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
this.resolveError(`Could not resolve the shorthand values`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import type { CliExtractor } from './cli/CliExtractor';
|
import type { CliExtractor } from './cli/CliExtractor';
|
||||||
import type { SettingsResolver } from './variables/SettingsResolver';
|
import type { ShorthandResolver } from './variables/ShorthandResolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that combines a {@link CliExtractor} and a {@link SettingsResolver}.
|
* A class that combines a {@link CliExtractor} and a {@link ShorthandResolver}.
|
||||||
* Mainly exists so both such classes can be generated in a single Components.js instance.
|
* Mainly exists so both such classes can be generated in a single Components.js instance.
|
||||||
*/
|
*/
|
||||||
export class CliResolver {
|
export class CliResolver {
|
||||||
public readonly cliExtractor: CliExtractor;
|
public readonly cliExtractor: CliExtractor;
|
||||||
public readonly settingsResolver: SettingsResolver;
|
public readonly shorthandResolver: ShorthandResolver;
|
||||||
|
|
||||||
public constructor(cliExtractor: CliExtractor, settingsResolver: SettingsResolver) {
|
public constructor(cliExtractor: CliExtractor, shorthandResolver: ShorthandResolver) {
|
||||||
this.cliExtractor = cliExtractor;
|
this.cliExtractor = cliExtractor;
|
||||||
this.settingsResolver = settingsResolver;
|
this.shorthandResolver = shorthandResolver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { AsyncHandler } from '../../util/handlers/AsyncHandler';
|
import { AsyncHandler } from '../../util/handlers/AsyncHandler';
|
||||||
import type { CliArgv, Settings } from '../variables/Types';
|
import type { CliArgv, Shorthand } from '../variables/Types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the input CLI arguments into an easily parseable key/value object.
|
* Converts the input CLI arguments into an easily parseable key/value object.
|
||||||
@ -15,4 +15,4 @@ import type { CliArgv, Settings } from '../variables/Types';
|
|||||||
* - -m / \--mainModulePath
|
* - -m / \--mainModulePath
|
||||||
* - -l / \--loggingLevel
|
* - -l / \--loggingLevel
|
||||||
*/
|
*/
|
||||||
export abstract class CliExtractor extends AsyncHandler<CliArgv, Settings> {}
|
export abstract class CliExtractor extends AsyncHandler<CliArgv, Shorthand> {}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { createErrorMessage } from '../../util/errors/ErrorUtil';
|
import { createErrorMessage } from '../../util/errors/ErrorUtil';
|
||||||
import type { SettingsExtractor } from './extractors/SettingsExtractor';
|
import type { ShorthandExtractor } from './extractors/ShorthandExtractor';
|
||||||
import { SettingsResolver } from './SettingsResolver';
|
import { ShorthandResolver } from './ShorthandResolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates variable values by running a set of {@link SettingsExtractor}s on the input.
|
* Generates variable values by running a set of {@link ShorthandExtractor}s on the input.
|
||||||
*/
|
*/
|
||||||
export class CombinedSettingsResolver extends SettingsResolver {
|
export class CombinedShorthandResolver extends ShorthandResolver {
|
||||||
public readonly resolvers: Record<string, SettingsExtractor>;
|
public readonly resolvers: Record<string, ShorthandExtractor>;
|
||||||
|
|
||||||
public constructor(resolvers: Record<string, SettingsExtractor>) {
|
public constructor(resolvers: Record<string, ShorthandExtractor>) {
|
||||||
super();
|
super();
|
||||||
this.resolvers = resolvers;
|
this.resolvers = resolvers;
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { AsyncHandler } from '../../util/handlers/AsyncHandler';
|
import { AsyncHandler } from '../../util/handlers/AsyncHandler';
|
||||||
import type { Settings, VariableBindings } from './Types';
|
import type { Shorthand, VariableBindings } from './Types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a key/value object, extracted from the CLI or passed as a parameter,
|
* Converts a key/value object, extracted from the CLI or passed as a parameter,
|
||||||
* into a new key/value object where the keys are variables defined in the Components.js configuration.
|
* into a new key/value object where the keys are variables defined in the Components.js configuration.
|
||||||
* The resulting values are the values that should be assigned to those variables.
|
* The resulting values are the values that should be assigned to those variables.
|
||||||
*/
|
*/
|
||||||
export abstract class SettingsResolver extends AsyncHandler<Settings, VariableBindings> {}
|
export abstract class ShorthandResolver extends AsyncHandler<Shorthand, VariableBindings> {}
|
@ -8,7 +8,7 @@ export type CliArgv = string[];
|
|||||||
/**
|
/**
|
||||||
* A key/value mapping of parsed command line arguments.
|
* A key/value mapping of parsed command line arguments.
|
||||||
*/
|
*/
|
||||||
export type Settings = Record<string, unknown>;
|
export type Shorthand = Record<string, unknown>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A key/value mapping of Components.js variables.
|
* A key/value mapping of Components.js variables.
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { resolveAssetPath } from '../../../util/PathUtil';
|
import { resolveAssetPath } from '../../../util/PathUtil';
|
||||||
import type { Settings } from '../Types';
|
import type { Shorthand } from '../Types';
|
||||||
import { SettingsExtractor } from './SettingsExtractor';
|
import { ShorthandExtractor } from './ShorthandExtractor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link SettingsExtractor} that converts a path value to an absolute asset path by making use of `resolveAssetPath`.
|
* A {@link ShorthandExtractor} that converts a path value to an absolute asset path
|
||||||
|
* by making use of `resolveAssetPath`.
|
||||||
* Returns the default path in case it is defined and no path was found in the map.
|
* Returns the default path in case it is defined and no path was found in the map.
|
||||||
*/
|
*/
|
||||||
export class AssetPathExtractor extends SettingsExtractor {
|
export class AssetPathExtractor extends ShorthandExtractor {
|
||||||
private readonly key: string;
|
private readonly key: string;
|
||||||
private readonly defaultPath?: string;
|
private readonly defaultPath?: string;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ export class AssetPathExtractor extends SettingsExtractor {
|
|||||||
this.defaultPath = defaultPath;
|
this.defaultPath = defaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(args: Settings): Promise<unknown> {
|
public async handle(args: Shorthand): Promise<unknown> {
|
||||||
const path = args[this.key] ?? this.defaultPath;
|
const path = args[this.key] ?? this.defaultPath;
|
||||||
if (path) {
|
if (path) {
|
||||||
if (typeof path !== 'string') {
|
if (typeof path !== 'string') {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { ensureTrailingSlash } from '../../../util/PathUtil';
|
import { ensureTrailingSlash } from '../../../util/PathUtil';
|
||||||
import type { Settings } from '../Types';
|
import type { Shorthand } from '../Types';
|
||||||
import { SettingsExtractor } from './SettingsExtractor';
|
import { ShorthandExtractor } from './ShorthandExtractor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link SettingsExtractor} that that generates the base URL based on the input `baseUrl` value,
|
* A {@link ShorthandExtractor} that that generates the base URL based on the input `baseUrl` value,
|
||||||
* or by using the port if the first isn't provided.
|
* or by using the port if the first isn't provided.
|
||||||
*/
|
*/
|
||||||
export class BaseUrlExtractor extends SettingsExtractor {
|
export class BaseUrlExtractor extends ShorthandExtractor {
|
||||||
private readonly defaultPort: number;
|
private readonly defaultPort: number;
|
||||||
|
|
||||||
public constructor(defaultPort = 3000) {
|
public constructor(defaultPort = 3000) {
|
||||||
@ -14,7 +14,7 @@ export class BaseUrlExtractor extends SettingsExtractor {
|
|||||||
this.defaultPort = defaultPort;
|
this.defaultPort = defaultPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(args: Settings): Promise<unknown> {
|
public async handle(args: Shorthand): Promise<unknown> {
|
||||||
if (typeof args.baseUrl === 'string') {
|
if (typeof args.baseUrl === 'string') {
|
||||||
return ensureTrailingSlash(args.baseUrl);
|
return ensureTrailingSlash(args.baseUrl);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import type { Settings } from '../Types';
|
import type { Shorthand } from '../Types';
|
||||||
import { SettingsExtractor } from './SettingsExtractor';
|
import { ShorthandExtractor } from './ShorthandExtractor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple {@link SettingsExtractor} that extracts a single value from the input map.
|
* A simple {@link ShorthandExtractor} that extracts a single value from the input map.
|
||||||
* Returns the default value if it was defined in case no value was found in the map.
|
* Returns the default value if it was defined in case no value was found in the map.
|
||||||
*/
|
*/
|
||||||
export class KeyExtractor extends SettingsExtractor {
|
export class KeyExtractor extends ShorthandExtractor {
|
||||||
private readonly key: string;
|
private readonly key: string;
|
||||||
private readonly defaultValue: unknown;
|
private readonly defaultValue: unknown;
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export class KeyExtractor extends SettingsExtractor {
|
|||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(args: Settings): Promise<unknown> {
|
public async handle(args: Shorthand): Promise<unknown> {
|
||||||
return typeof args[this.key] === 'undefined' ? this.defaultValue : args[this.key];
|
return typeof args[this.key] === 'undefined' ? this.defaultValue : args[this.key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
|
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
|
||||||
import type { Settings } from '../Types';
|
import type { Shorthand } from '../Types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A handler that computes a specific value from a given map of values.
|
* A handler that computes a specific value from a given map of values.
|
||||||
*/
|
*/
|
||||||
export abstract class SettingsExtractor extends AsyncHandler<Settings, unknown> {}
|
export abstract class ShorthandExtractor extends AsyncHandler<Shorthand, unknown> {}
|
@ -3,7 +3,7 @@ import type { ClusterManager } from '../../../src';
|
|||||||
import type { App } from '../../../src/init/App';
|
import type { App } from '../../../src/init/App';
|
||||||
import { AppRunner } from '../../../src/init/AppRunner';
|
import { AppRunner } from '../../../src/init/AppRunner';
|
||||||
import type { CliExtractor } from '../../../src/init/cli/CliExtractor';
|
import type { CliExtractor } from '../../../src/init/cli/CliExtractor';
|
||||||
import type { SettingsResolver } from '../../../src/init/variables/SettingsResolver';
|
import type { ShorthandResolver } from '../../../src/init/variables/ShorthandResolver';
|
||||||
import { joinFilePath } from '../../../src/util/PathUtil';
|
import { joinFilePath } from '../../../src/util/PathUtil';
|
||||||
import { flushPromises } from '../../util/Util';
|
import { flushPromises } from '../../util/Util';
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ const defaultVariables = {
|
|||||||
'urn:solid-server:default:variable:port': 3000,
|
'urn:solid-server:default:variable:port': 3000,
|
||||||
'urn:solid-server:default:variable:loggingLevel': 'info',
|
'urn:solid-server:default:variable:loggingLevel': 'info',
|
||||||
};
|
};
|
||||||
const settingsResolver: jest.Mocked<SettingsResolver> = {
|
const shorthandResolver: jest.Mocked<ShorthandResolver> = {
|
||||||
handleSafe: jest.fn().mockResolvedValue(defaultVariables),
|
handleSafe: jest.fn().mockResolvedValue(defaultVariables),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ const app: jest.Mocked<App> = {
|
|||||||
const manager: jest.Mocked<ComponentsManager<App>> = {
|
const manager: jest.Mocked<ComponentsManager<App>> = {
|
||||||
instantiate: jest.fn(async(iri: string): Promise<any> => {
|
instantiate: jest.fn(async(iri: string): Promise<any> => {
|
||||||
switch (iri) {
|
switch (iri) {
|
||||||
case 'urn:solid-server-app-setup:default:CliResolver': return { cliExtractor, settingsResolver };
|
case 'urn:solid-server-app-setup:default:CliResolver': return { cliExtractor, shorthandResolver };
|
||||||
case 'urn:solid-server:default:App': return app;
|
case 'urn:solid-server:default:App': return app;
|
||||||
default: throw new Error('unknown iri');
|
default: throw new Error('unknown iri');
|
||||||
}
|
}
|
||||||
@ -86,13 +86,20 @@ describe('AppRunner', (): void => {
|
|||||||
describe('create', (): void => {
|
describe('create', (): void => {
|
||||||
it('creates an App with the provided settings.', async(): Promise<void> => {
|
it('creates an App with the provided settings.', async(): Promise<void> => {
|
||||||
const variables = {
|
const variables = {
|
||||||
'urn:solid-server:default:variable:port': 3000,
|
'urn:solid-server:default:variable:port': 4000,
|
||||||
'urn:solid-server:default:variable:loggingLevel': 'info',
|
|
||||||
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
|
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
|
||||||
'urn:solid-server:default:variable:showStackTrace': false,
|
'urn:solid-server:default:variable:showStackTrace': false,
|
||||||
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
|
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
|
||||||
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
|
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
|
||||||
};
|
};
|
||||||
|
const shorthand = {
|
||||||
|
logLevel: 'info',
|
||||||
|
};
|
||||||
|
const expectedVariables = {
|
||||||
|
...variables,
|
||||||
|
'urn:solid-server:default:variable:loggingLevel': 'info',
|
||||||
|
};
|
||||||
|
|
||||||
const createdApp = await new AppRunner().create(
|
const createdApp = await new AppRunner().create(
|
||||||
{
|
{
|
||||||
mainModulePath: joinFilePath(__dirname, '../../../'),
|
mainModulePath: joinFilePath(__dirname, '../../../'),
|
||||||
@ -101,6 +108,7 @@ describe('AppRunner', (): void => {
|
|||||||
},
|
},
|
||||||
joinFilePath(__dirname, '../../../config/default.json'),
|
joinFilePath(__dirname, '../../../config/default.json'),
|
||||||
variables,
|
variables,
|
||||||
|
shorthand,
|
||||||
);
|
);
|
||||||
expect(createdApp).toBe(app);
|
expect(createdApp).toBe(app);
|
||||||
|
|
||||||
@ -113,10 +121,14 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.configRegistry.register)
|
expect(manager.configRegistry.register)
|
||||||
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
||||||
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
expect(manager.instantiate).toHaveBeenCalledTimes(2);
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server:default:App', { variables });
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(
|
||||||
|
2, 'urn:solid-server:default:App', { variables: expectedVariables },
|
||||||
|
);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenLastCalledWith(shorthand);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(0);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(0);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(0);
|
|
||||||
expect(app.start).toHaveBeenCalledTimes(0);
|
expect(app.start).toHaveBeenCalledTimes(0);
|
||||||
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
||||||
});
|
});
|
||||||
@ -193,13 +205,20 @@ describe('AppRunner', (): void => {
|
|||||||
describe('run', (): void => {
|
describe('run', (): void => {
|
||||||
it('starts the server with provided settings.', async(): Promise<void> => {
|
it('starts the server with provided settings.', async(): Promise<void> => {
|
||||||
const variables = {
|
const variables = {
|
||||||
'urn:solid-server:default:variable:port': 3000,
|
'urn:solid-server:default:variable:port': 4000,
|
||||||
'urn:solid-server:default:variable:loggingLevel': 'info',
|
|
||||||
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
|
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
|
||||||
'urn:solid-server:default:variable:showStackTrace': false,
|
'urn:solid-server:default:variable:showStackTrace': false,
|
||||||
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
|
'urn:solid-server:default:variable:podConfigJson': '/var/cwd/pod-config.json',
|
||||||
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
|
'urn:solid-server:default:variable:seededPodConfigJson': '/var/cwd/seeded-pod-config.json',
|
||||||
};
|
};
|
||||||
|
const shorthand = {
|
||||||
|
logLevel: 'info',
|
||||||
|
};
|
||||||
|
const expectedVariables = {
|
||||||
|
...variables,
|
||||||
|
'urn:solid-server:default:variable:loggingLevel': 'info',
|
||||||
|
};
|
||||||
|
|
||||||
await new AppRunner().run(
|
await new AppRunner().run(
|
||||||
{
|
{
|
||||||
mainModulePath: joinFilePath(__dirname, '../../../'),
|
mainModulePath: joinFilePath(__dirname, '../../../'),
|
||||||
@ -208,6 +227,7 @@ describe('AppRunner', (): void => {
|
|||||||
},
|
},
|
||||||
joinFilePath(__dirname, '../../../config/default.json'),
|
joinFilePath(__dirname, '../../../config/default.json'),
|
||||||
variables,
|
variables,
|
||||||
|
shorthand,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(ComponentsManager.build).toHaveBeenCalledTimes(1);
|
expect(ComponentsManager.build).toHaveBeenCalledTimes(1);
|
||||||
@ -219,10 +239,14 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.configRegistry.register)
|
expect(manager.configRegistry.register)
|
||||||
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
||||||
expect(manager.instantiate).toHaveBeenCalledTimes(1);
|
expect(manager.instantiate).toHaveBeenCalledTimes(2);
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server:default:App', { variables });
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(
|
||||||
|
2, 'urn:solid-server:default:App', { variables: expectedVariables },
|
||||||
|
);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenLastCalledWith(shorthand);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(0);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(0);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(0);
|
|
||||||
expect(app.start).toHaveBeenCalledTimes(1);
|
expect(app.start).toHaveBeenCalledTimes(1);
|
||||||
expect(app.start).toHaveBeenCalledWith();
|
expect(app.start).toHaveBeenCalledWith();
|
||||||
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
||||||
@ -247,8 +271,9 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(1);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
||||||
'urn:solid-server:default:App',
|
'urn:solid-server:default:App',
|
||||||
{ variables: defaultVariables });
|
{ variables: defaultVariables });
|
||||||
@ -289,8 +314,9 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledWith(argvParameters);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledWith(argvParameters);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(1);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
||||||
'urn:solid-server:default:App',
|
'urn:solid-server:default:App',
|
||||||
{ variables: defaultVariables });
|
{ variables: defaultVariables });
|
||||||
@ -349,7 +375,39 @@ describe('AppRunner', (): void => {
|
|||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
caughtError = error as Error;
|
caughtError = error as Error;
|
||||||
}
|
}
|
||||||
expect(caughtError.message).toMatch(/^Could not load the config variables/mu);
|
expect(caughtError.message).toMatch(/^Could not create the CLI resolver/mu);
|
||||||
|
expect(caughtError.message).toMatch(/^Cause: Fatal/mu);
|
||||||
|
|
||||||
|
expect(write).toHaveBeenCalledTimes(0);
|
||||||
|
expect(exit).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if extracting the CLI shorthand values fails.', async(): Promise<void> => {
|
||||||
|
cliExtractor.handleSafe.mockRejectedValueOnce(new Error('Fatal'));
|
||||||
|
|
||||||
|
let caughtError: Error = new Error('should disappear');
|
||||||
|
try {
|
||||||
|
await new AppRunner().createCli([ 'node', 'script' ]);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
caughtError = error as Error;
|
||||||
|
}
|
||||||
|
expect(caughtError.message).toMatch(/^Could not parse the CLI parameters/mu);
|
||||||
|
expect(caughtError.message).toMatch(/^Cause: Fatal/mu);
|
||||||
|
|
||||||
|
expect(write).toHaveBeenCalledTimes(0);
|
||||||
|
expect(exit).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if resolving the shorthand values fails.', async(): Promise<void> => {
|
||||||
|
shorthandResolver.handleSafe.mockRejectedValueOnce(new Error('Fatal'));
|
||||||
|
|
||||||
|
let caughtError: Error = new Error('should disappear');
|
||||||
|
try {
|
||||||
|
await new AppRunner().createCli([ 'node', 'script' ]);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
caughtError = error as Error;
|
||||||
|
}
|
||||||
|
expect(caughtError.message).toMatch(/^Could not resolve the shorthand values/mu);
|
||||||
expect(caughtError.message).toMatch(/^Cause: Fatal/mu);
|
expect(caughtError.message).toMatch(/^Cause: Fatal/mu);
|
||||||
|
|
||||||
expect(write).toHaveBeenCalledTimes(0);
|
expect(write).toHaveBeenCalledTimes(0);
|
||||||
@ -359,7 +417,7 @@ describe('AppRunner', (): void => {
|
|||||||
it('throws an error if instantiating the server fails.', async(): Promise<void> => {
|
it('throws an error if instantiating the server fails.', async(): Promise<void> => {
|
||||||
// We want the second call to fail
|
// We want the second call to fail
|
||||||
manager.instantiate
|
manager.instantiate
|
||||||
.mockResolvedValueOnce({ cliExtractor, settingsResolver })
|
.mockResolvedValueOnce({ cliExtractor, shorthandResolver })
|
||||||
.mockRejectedValueOnce(new Error('Fatal'));
|
.mockRejectedValueOnce(new Error('Fatal'));
|
||||||
|
|
||||||
let caughtError: Error = new Error('should disappear');
|
let caughtError: Error = new Error('should disappear');
|
||||||
@ -409,8 +467,9 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(1);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
||||||
'urn:solid-server:default:App',
|
'urn:solid-server:default:App',
|
||||||
{ variables: defaultVariables });
|
{ variables: defaultVariables });
|
||||||
@ -419,6 +478,45 @@ describe('AppRunner', (): void => {
|
|||||||
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
expect(app.clusterManager.isSingleThreaded()).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('runs the server honoring env variables.', async(): Promise<void> => {
|
||||||
|
// Set logging level to debug
|
||||||
|
const { env } = process;
|
||||||
|
const OLD_STATE = env.CSS_LOGGING_LEVEL;
|
||||||
|
env.CSS_LOGGING_LEVEL = 'debug';
|
||||||
|
await expect(new AppRunner().runCli([ 'node', 'script' ])).resolves.toBeUndefined();
|
||||||
|
|
||||||
|
expect(ComponentsManager.build).toHaveBeenCalledTimes(1);
|
||||||
|
// Check logLevel to be set to debug instead of default `info`
|
||||||
|
expect(ComponentsManager.build).toHaveBeenCalledWith({
|
||||||
|
dumpErrorState: true,
|
||||||
|
logLevel: 'info',
|
||||||
|
mainModulePath: joinFilePath(__dirname, '../../../'),
|
||||||
|
typeChecking: false,
|
||||||
|
});
|
||||||
|
expect(manager.configRegistry.register).toHaveBeenCalledTimes(1);
|
||||||
|
expect(manager.configRegistry.register)
|
||||||
|
.toHaveBeenCalledWith(joinFilePath(__dirname, '/../../../config/default.json'));
|
||||||
|
expect(manager.instantiate).toHaveBeenCalledTimes(2);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
||||||
|
'urn:solid-server:default:App',
|
||||||
|
{ variables: defaultVariables });
|
||||||
|
expect(app.start).toHaveBeenCalledTimes(1);
|
||||||
|
expect(app.start).toHaveBeenLastCalledWith();
|
||||||
|
|
||||||
|
// Reset env
|
||||||
|
if (OLD_STATE) {
|
||||||
|
env.CSS_LOGGING_LEVEL = OLD_STATE;
|
||||||
|
} else {
|
||||||
|
delete env.CSS_LOGGING_LEVEL;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('throws an error if the server could not start.', async(): Promise<void> => {
|
it('throws an error if the server could not start.', async(): Promise<void> => {
|
||||||
app.start.mockRejectedValueOnce(new Error('Fatal'));
|
app.start.mockRejectedValueOnce(new Error('Fatal'));
|
||||||
|
|
||||||
@ -461,8 +559,9 @@ describe('AppRunner', (): void => {
|
|||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
expect(cliExtractor.handleSafe).toHaveBeenCalledWith([ 'node', 'script' ]);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledTimes(1);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledTimes(1);
|
||||||
expect(settingsResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
expect(shorthandResolver.handleSafe).toHaveBeenCalledWith(defaultParameters);
|
||||||
|
expect(manager.instantiate).toHaveBeenNthCalledWith(1, 'urn:solid-server-app-setup:default:CliResolver', {});
|
||||||
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
expect(manager.instantiate).toHaveBeenNthCalledWith(2,
|
||||||
'urn:solid-server:default:App',
|
'urn:solid-server:default:App',
|
||||||
{ variables: defaultVariables });
|
{ variables: defaultVariables });
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import type { CliExtractor } from '../../../src/init/cli/CliExtractor';
|
import type { CliExtractor } from '../../../src/init/cli/CliExtractor';
|
||||||
import { CliResolver } from '../../../src/init/CliResolver';
|
import { CliResolver } from '../../../src/init/CliResolver';
|
||||||
import type { SettingsResolver } from '../../../src/init/variables/SettingsResolver';
|
import type { ShorthandResolver } from '../../../src/init/variables/ShorthandResolver';
|
||||||
|
|
||||||
describe('A CliResolver', (): void => {
|
describe('A CliResolver', (): void => {
|
||||||
it('stores a CliExtractor and SettingsResolver.', async(): Promise<void> => {
|
it('stores a CliExtractor and SettingsResolver.', async(): Promise<void> => {
|
||||||
const cliExtractor: CliExtractor = { canHandle: jest.fn().mockResolvedValue('CLI!') } as any;
|
const cliExtractor: CliExtractor = { canHandle: jest.fn().mockResolvedValue('CLI!') } as any;
|
||||||
const settingsResolver: SettingsResolver = { canHandle: jest.fn().mockResolvedValue('Settings!') } as any;
|
const settingsResolver: ShorthandResolver = { canHandle: jest.fn().mockResolvedValue('Settings!') } as any;
|
||||||
const cliResolver = new CliResolver(cliExtractor, settingsResolver);
|
const cliResolver = new CliResolver(cliExtractor, settingsResolver);
|
||||||
expect(cliResolver.cliExtractor).toBe(cliExtractor);
|
expect(cliResolver.cliExtractor).toBe(cliExtractor);
|
||||||
expect(cliResolver.settingsResolver).toBe(settingsResolver);
|
expect(cliResolver.shorthandResolver).toBe(settingsResolver);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { CombinedSettingsResolver } from '../../../../src/init/variables/CombinedSettingsResolver';
|
import { CombinedShorthandResolver } from '../../../../src/init/variables/CombinedShorthandResolver';
|
||||||
import type { SettingsExtractor } from '../../../../src/init/variables/extractors/SettingsExtractor';
|
import type { ShorthandExtractor } from '../../../../src/init/variables/extractors/ShorthandExtractor';
|
||||||
|
|
||||||
describe('A CombinedSettingsResolver', (): void => {
|
describe('A CombinedShorthandResolver', (): void => {
|
||||||
const values = { test: 'data' };
|
const values = { test: 'data' };
|
||||||
const varPort = 'urn:solid-server:default:variable:port';
|
const varPort = 'urn:solid-server:default:variable:port';
|
||||||
const varLog = 'urn:solid-server:default:variable:loggingLevel';
|
const varLog = 'urn:solid-server:default:variable:loggingLevel';
|
||||||
let resolverPort: jest.Mocked<SettingsExtractor>;
|
let resolverPort: jest.Mocked<ShorthandExtractor>;
|
||||||
let resolverLog: jest.Mocked<SettingsExtractor>;
|
let resolverLog: jest.Mocked<ShorthandExtractor>;
|
||||||
let resolver: CombinedSettingsResolver;
|
let resolver: CombinedShorthandResolver;
|
||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
resolverPort = {
|
resolverPort = {
|
||||||
@ -18,7 +18,7 @@ describe('A CombinedSettingsResolver', (): void => {
|
|||||||
handleSafe: jest.fn().mockResolvedValue('info'),
|
handleSafe: jest.fn().mockResolvedValue('info'),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
resolver = new CombinedSettingsResolver({
|
resolver = new CombinedShorthandResolver({
|
||||||
[varPort]: resolverPort,
|
[varPort]: resolverPort,
|
||||||
[varLog]: resolverLog,
|
[varLog]: resolverLog,
|
||||||
});
|
});
|
Loading…
x
Reference in New Issue
Block a user