feat: Add showStackTrace CLI variable

This commit is contained in:
Joachim Van Herwegen 2021-06-07 13:36:52 +02:00
parent 57d77e941d
commit b604dd8331
13 changed files with 49 additions and 45 deletions

View File

@ -69,6 +69,7 @@ Additional recipes for configuring and deploying the server can be found at [sol
| `--loggingLevel, -l` | `"info"`| | | `--loggingLevel, -l` | `"info"`| |
| `--rootFilePath, -f` | `"./"` | Folder to start the server in when using a file-based config. | | `--rootFilePath, -f` | `"./"` | Folder to start the server in when using a file-based config. |
| `--sparqlEndpoint, -s` | | Endpoint to call when using a SPARQL-based config. | | `--sparqlEndpoint, -s` | | Endpoint to call when using a SPARQL-based config. |
| `--showStackTrace, -t` | false | Whether error stack traces should be shown in responses. |
| `--podConfigJson` | `"./pod-config.json"` | JSON file to store pod configuration when using a dynamic config. | | `--podConfigJson` | `"./pod-config.json"` | JSON file to store pod configuration when using a dynamic config. |
| `--idpTemplateFolder` | `"templates/idp"` | Folder containing the templates used for IDP interactions. | | `--idpTemplateFolder` | `"templates/idp"` | Folder containing the templates used for IDP interactions. |

View File

@ -44,41 +44,32 @@ export class AppRunner {
} = {}): void { } = {}): void {
// Parse the command-line arguments // Parse the command-line arguments
const { argv: params } = yargs(argv.slice(2)) const { argv: params } = yargs(argv.slice(2))
.strict()
.usage('node ./bin/server.js [args]') .usage('node ./bin/server.js [args]')
.check((args, options): boolean => { .check((args): boolean => {
// Only take flags as arguments, not filenames if (args._.length > 0) {
if (args._ && args._.length > 0) { throw new Error(`Unsupported positional arguments: "${args._.join('", "')}"`);
throw new Error(`Unsupported arguments: ${args._.join('", "')}`);
}
for (const key in args) {
// Skip filename arguments (_) and the script name ($0)
if (key !== '_' && key !== '$0') {
// Check if the argument occurs in the provided options list
if (!options[key]) {
throw new Error(`Unknown option: "${key}"`);
}
// Check if the argument actually has a value ('> ./bin/server.js -s' is not valid)
if (!args[key]) {
throw new Error(`Missing value for argument "${key}"`);
}
// Check if the argument only has 1 value
if (Array.isArray(args[key])) {
throw new Error(`Multiple values were provided for: "${key}", [${args[key]}]`);
} }
for (const key of Object.keys(args)) {
// We have no options that allow for arrays
const val = args[key];
if (key !== '_' && Array.isArray(val)) {
throw new Error(`Multiple values were provided for: "${key}": "${val.join('", "')}"`);
} }
} }
return true; return true;
}) })
.options({ .options({
baseUrl: { type: 'string', alias: 'b' }, baseUrl: { type: 'string', alias: 'b', requiresArg: true },
config: { type: 'string', alias: 'c' }, config: { type: 'string', alias: 'c', requiresArg: true },
loggingLevel: { type: 'string', alias: 'l', default: 'info' }, loggingLevel: { type: 'string', alias: 'l', default: 'info', requiresArg: true },
mainModulePath: { type: 'string', alias: 'm' }, mainModulePath: { type: 'string', alias: 'm', requiresArg: true },
idpTemplateFolder: { type: 'string' }, idpTemplateFolder: { type: 'string', requiresArg: true },
port: { type: 'number', alias: 'p', default: 3000 }, port: { type: 'number', alias: 'p', default: 3000, requiresArg: true },
rootFilePath: { type: 'string', alias: 'f', default: './' }, rootFilePath: { type: 'string', alias: 'f', default: './', requiresArg: true },
sparqlEndpoint: { type: 'string', alias: 's' }, showStackTrace: { type: 'boolean', alias: 't', default: false },
podConfigJson: { type: 'string', default: './pod-config.json' }, sparqlEndpoint: { type: 'string', alias: 's', requiresArg: true },
podConfigJson: { type: 'string', default: './pod-config.json', requiresArg: true },
}) })
.help(); .help();
@ -129,6 +120,7 @@ export class AppRunner {
'urn:solid-server:default:variable:rootFilePath': 'urn:solid-server:default:variable:rootFilePath':
this.resolveFilePath(params.rootFilePath), this.resolveFilePath(params.rootFilePath),
'urn:solid-server:default:variable:sparqlEndpoint': params.sparqlEndpoint, 'urn:solid-server:default:variable:sparqlEndpoint': params.sparqlEndpoint,
'urn:solid-server:default:variable:showStackTrace': params.showStackTrace,
'urn:solid-server:default:variable:podConfigJson': 'urn:solid-server:default:variable:podConfigJson':
this.resolveFilePath(params.podConfigJson), this.resolveFilePath(params.podConfigJson),
'urn:solid-server:default:variable:idpTemplateFolder': 'urn:solid-server:default:variable:idpTemplateFolder':
@ -158,6 +150,7 @@ export interface ConfigVariables {
baseUrl?: string; baseUrl?: string;
rootFilePath?: string; rootFilePath?: string;
sparqlEndpoint?: string; sparqlEndpoint?: string;
showStackTrace?: boolean;
podConfigJson?: string; podConfigJson?: string;
idpTemplateFolder?: string; idpTemplateFolder?: string;
} }

View File

@ -38,6 +38,7 @@ describe.each(configs)('A dynamic pod server with template config %s', (template
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:rootFilePath': rootFilePath, 'urn:solid-server:default:variable:rootFilePath': rootFilePath,
'urn:solid-server:default:variable:podConfigJson': podConfigJson, 'urn:solid-server:default:variable:podConfigJson': podConfigJson,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'), 'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
}; };

View File

@ -63,6 +63,7 @@ describe('A Solid server with IDP', (): void => {
getTestConfigPath('server-memory.json'), getTestConfigPath('server-memory.json'),
{ {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'), 'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
}, },
) as Record<string, any>; ) as Record<string, any>;

View File

@ -40,6 +40,7 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
beforeAll(async(): Promise<void> => { beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = { const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:rootFilePath': rootFilePath, 'urn:solid-server:default:variable:rootFilePath': rootFilePath,
}; };

View File

@ -34,6 +34,7 @@ describe.each(stores)('An LDP handler allowing all requests %s', (name, { storeC
beforeAll(async(): Promise<void> => { beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = { const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:rootFilePath': rootFilePath, 'urn:solid-server:default:variable:rootFilePath': rootFilePath,
}; };

View File

@ -25,6 +25,7 @@ describe('An http server with middleware', (): void => {
{ {
'urn:solid-server:default:LdpHandler': new SimpleHttpHandler(), 'urn:solid-server:default:LdpHandler': new SimpleHttpHandler(),
'urn:solid-server:default:variable:baseUrl': 'https://example.pod/', 'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
'urn:solid-server:default:variable:showStackTrace': true,
}, },
) as BaseHttpServerFactory; ) as BaseHttpServerFactory;
server = factory.startServer(port); server = factory.startServer(port);

View File

@ -21,6 +21,7 @@ describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', ()
getTestConfigPath('run-with-redlock.json'), getTestConfigPath('run-with-redlock.json'),
{ {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
}, },
) as Record<string, any>; ) as Record<string, any>;
({ factory, locker } = instances); ({ factory, locker } = instances);

View File

@ -22,6 +22,7 @@ describe('A Solid server', (): void => {
getTestConfigPath('server-memory.json'), getTestConfigPath('server-memory.json'),
{ {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': '', 'urn:solid-server:default:variable:idpTemplateFolder': '',
}, },
) as Record<string, any>; ) as Record<string, any>;

View File

@ -18,6 +18,7 @@ describeIf('docker', 'A server with a SPARQL endpoint as storage', (): void => {
beforeAll(async(): Promise<void> => { beforeAll(async(): Promise<void> => {
const variables: Record<string, any> = { const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql', 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:4000/sparql',
}; };

View File

@ -37,6 +37,7 @@ describe.each(stores)('A subdomain server with %s', (name, { storeConfig, teardo
const variables: Record<string, any> = { const variables: Record<string, any> = {
'urn:solid-server:default:variable:baseUrl': baseUrl, 'urn:solid-server:default:variable:baseUrl': baseUrl,
'urn:solid-server:default:variable:rootFilePath': rootFilePath, 'urn:solid-server:default:variable:rootFilePath': rootFilePath,
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'), 'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../templates/idp'),
}; };

View File

@ -18,6 +18,7 @@ describe('A server with the Solid WebSockets API behind a proxy', (): void => {
getTestConfigPath('server-without-auth.json'), getTestConfigPath('server-without-auth.json'),
{ {
'urn:solid-server:default:variable:baseUrl': 'https://example.pod/', 'urn:solid-server:default:variable:baseUrl': 'https://example.pod/',
'urn:solid-server:default:variable:showStackTrace': true,
}, },
) as HttpServerFactory; ) as HttpServerFactory;
server = factory.startServer(port); server = factory.startServer(port);

View File

@ -44,6 +44,7 @@ describe('AppRunner', (): void => {
port: 3000, port: 3000,
loggingLevel: 'info', loggingLevel: 'info',
rootFilePath: '/var/cwd/', rootFilePath: '/var/cwd/',
showStackTrace: false,
podConfigJson: '/var/cwd/pod-config.json', podConfigJson: '/var/cwd/pod-config.json',
}, },
); );
@ -67,6 +68,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/', 'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
'urn:solid-server:default:variable:sparqlEndpoint': undefined, 'urn:solid-server:default:variable:sparqlEndpoint': undefined,
'urn:solid-server:default:variable:loggingLevel': 'info', 'urn:solid-server:default:variable:loggingLevel': 'info',
'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:idpTemplateFolder': joinFilePath(__dirname, '../../../templates/idp'), 'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../../templates/idp'),
}, },
@ -107,6 +109,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/', 'urn:solid-server:default:variable:rootFilePath': '/var/cwd/',
'urn:solid-server:default:variable:sparqlEndpoint': undefined, 'urn:solid-server:default:variable:sparqlEndpoint': undefined,
'urn:solid-server:default:variable:loggingLevel': 'info', 'urn:solid-server:default:variable:loggingLevel': 'info',
'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:idpTemplateFolder': joinFilePath(__dirname, '../../../templates/idp'), 'urn:solid-server:default:variable:idpTemplateFolder': joinFilePath(__dirname, '../../../templates/idp'),
}, },
@ -127,6 +130,7 @@ describe('AppRunner', (): void => {
'-m', 'module/path', '-m', 'module/path',
'-p', '4000', '-p', '4000',
'-s', 'http://localhost:5000/sparql', '-s', 'http://localhost:5000/sparql',
'-t',
'--podConfigJson', '/different-path.json', '--podConfigJson', '/different-path.json',
'--idpTemplateFolder', 'templates/idp', '--idpTemplateFolder', 'templates/idp',
], ],
@ -154,6 +158,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:port': 4000, 'urn:solid-server:default:variable:port': 4000,
'urn:solid-server:default:variable:rootFilePath': '/root', 'urn:solid-server:default:variable:rootFilePath': '/root',
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql', 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql',
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:podConfigJson': '/different-path.json', 'urn:solid-server:default:variable:podConfigJson': '/different-path.json',
'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp', 'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp',
}, },
@ -172,6 +177,7 @@ describe('AppRunner', (): void => {
'--port', '4000', '--port', '4000',
'--rootFilePath', 'root', '--rootFilePath', 'root',
'--sparqlEndpoint', 'http://localhost:5000/sparql', '--sparqlEndpoint', 'http://localhost:5000/sparql',
'--showStackTrace',
'--podConfigJson', '/different-path.json', '--podConfigJson', '/different-path.json',
'--idpTemplateFolder', 'templates/idp', '--idpTemplateFolder', 'templates/idp',
], ],
@ -199,6 +205,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:port': 4000, 'urn:solid-server:default:variable:port': 4000,
'urn:solid-server:default:variable:rootFilePath': '/var/cwd/root', 'urn:solid-server:default:variable:rootFilePath': '/var/cwd/root',
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql', 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql',
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:podConfigJson': '/different-path.json', 'urn:solid-server:default:variable:podConfigJson': '/different-path.json',
'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp', 'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp',
}, },
@ -217,6 +224,7 @@ describe('AppRunner', (): void => {
'-m', 'module/path', '-m', 'module/path',
'-p', '4000', '-p', '4000',
'-s', 'http://localhost:5000/sparql', '-s', 'http://localhost:5000/sparql',
'-t',
'--podConfigJson', '/different-path.json', '--podConfigJson', '/different-path.json',
'--idpTemplateFolder', 'templates/idp', '--idpTemplateFolder', 'templates/idp',
]; ];
@ -245,6 +253,7 @@ describe('AppRunner', (): void => {
'urn:solid-server:default:variable:port': 4000, 'urn:solid-server:default:variable:port': 4000,
'urn:solid-server:default:variable:rootFilePath': '/root', 'urn:solid-server:default:variable:rootFilePath': '/root',
'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql', 'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql',
'urn:solid-server:default:variable:showStackTrace': true,
'urn:solid-server:default:variable:podConfigJson': '/different-path.json', 'urn:solid-server:default:variable:podConfigJson': '/different-path.json',
'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp', 'urn:solid-server:default:variable:idpTemplateFolder': '/var/cwd/templates/idp',
}, },
@ -293,9 +302,7 @@ describe('AppRunner', (): void => {
it('exits when unknown options are passed to the main executable.', async(): Promise<void> => { it('exits when unknown options are passed to the main executable.', async(): Promise<void> => {
new AppRunner().runCli({ new AppRunner().runCli({
argv: [ argv: [ 'node', 'script', '--foo' ],
'node', 'script', '--foo',
],
}); });
// Wait until initializer has been called, because we can't await AppRunner.run. // Wait until initializer has been called, because we can't await AppRunner.run.
@ -303,16 +310,14 @@ describe('AppRunner', (): void => {
setImmediate(resolve); setImmediate(resolve);
}); });
expect(error).toHaveBeenCalledWith('Unknown option: "foo"'); expect(error).toHaveBeenCalledWith('Unknown argument: foo');
expect(exit).toHaveBeenCalledTimes(1); expect(exit).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1); expect(exit).toHaveBeenCalledWith(1);
}); });
it('exits when no value is passed to the main executable for an argument.', async(): Promise<void> => { it('exits when no value is passed to the main executable for an argument.', async(): Promise<void> => {
new AppRunner().runCli({ new AppRunner().runCli({
argv: [ argv: [ 'node', 'script', '-s' ],
'node', 'script', '-s',
],
}); });
// Wait until initializer has been called, because we can't await AppRunner.run. // Wait until initializer has been called, because we can't await AppRunner.run.
@ -320,16 +325,14 @@ describe('AppRunner', (): void => {
setImmediate(resolve); setImmediate(resolve);
}); });
expect(error).toHaveBeenCalledWith('Missing value for argument "s"'); expect(error).toHaveBeenCalledWith('Not enough arguments following: s');
expect(exit).toHaveBeenCalledTimes(1); expect(exit).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1); expect(exit).toHaveBeenCalledWith(1);
}); });
it('exits when unknown parameters are passed to the main executable.', async(): Promise<void> => { it('exits when unknown parameters are passed to the main executable.', async(): Promise<void> => {
new AppRunner().runCli({ new AppRunner().runCli({
argv: [ argv: [ 'node', 'script', 'foo', 'bar', 'foo.txt', 'bar.txt' ],
'node', 'script', 'foo', 'bar', 'foo.txt', 'bar.txt',
],
}); });
// Wait until initializer has been called, because we can't await AppRunner.run. // Wait until initializer has been called, because we can't await AppRunner.run.
@ -337,17 +340,14 @@ describe('AppRunner', (): void => {
setImmediate(resolve); setImmediate(resolve);
}); });
// There seems to be an issue with yargs where the first and last `"` are missing. expect(error).toHaveBeenCalledWith('Unsupported positional arguments: "foo", "bar", "foo.txt", "bar.txt"');
expect(error).toHaveBeenCalledWith('Unsupported arguments: foo", "bar", "foo.txt", "bar.txt');
expect(exit).toHaveBeenCalledTimes(1); expect(exit).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1); expect(exit).toHaveBeenCalledWith(1);
}); });
it('exits when multiple values for a parameter are passed.', async(): Promise<void> => { it('exits when multiple values for a parameter are passed.', async(): Promise<void> => {
new AppRunner().runCli({ new AppRunner().runCli({
argv: [ argv: [ 'node', 'script', '-l', 'info', '-l', 'debug' ],
'node', 'script', '-ll',
],
}); });
// Wait until initializer has been called, because we can't await AppRunner.run. // Wait until initializer has been called, because we can't await AppRunner.run.
@ -355,7 +355,7 @@ describe('AppRunner', (): void => {
setImmediate(resolve); setImmediate(resolve);
}); });
expect(error).toHaveBeenCalledWith('Multiple values were provided for: "l", [info,info]'); expect(error).toHaveBeenCalledWith('Multiple values were provided for: "l": "info", "debug"');
expect(exit).toHaveBeenCalledTimes(1); expect(exit).toHaveBeenCalledTimes(1);
expect(exit).toHaveBeenCalledWith(1); expect(exit).toHaveBeenCalledWith(1);
}); });