feat: Accept both Settings and VariableBindings to create an App

This commit is contained in:
Joachim Van Herwegen 2022-07-15 15:27:35 +02:00
parent 8609704528
commit f609f1a9c5
19 changed files with 286 additions and 131 deletions

View File

@ -19,7 +19,7 @@
"Promise", "Promise",
"Readonly", "Readonly",
"RegExp", "RegExp",
"Settings", "Shorthand",
"Template", "Template",
"TemplateEngine", "TemplateEngine",
"ValuePreferencesArg", "ValuePreferencesArg",

View File

@ -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.

View File

@ -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" }
} }
] ]
} }

View File

@ -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

View File

@ -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"
} }

View File

@ -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';

View File

@ -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);
} }
} }

View File

@ -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;
} }
} }

View File

@ -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> {}

View File

@ -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;
} }

View File

@ -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> {}

View File

@ -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.

View File

@ -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') {

View File

@ -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);
} }

View File

@ -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];
} }
} }

View File

@ -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> {}

View File

@ -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 });

View File

@ -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);
}); });
}); });

View File

@ -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,
}); });