From f204fa336581381b211fccd03248a8ab46ba458d Mon Sep 17 00:00:00 2001 From: Alec Marcum Date: Fri, 8 Jan 2021 14:25:01 -0800 Subject: [PATCH] feat!: Support detailed error objects in cli (#108) Co-authored-by: Alec Co-authored-by: Jennifer Mah <42650198+JenniferMah@users.noreply.github.com> --- src/base-commands/base-command.js | 8 ++++++-- src/services/cli-http-client.js | 21 ++++++++++++++++----- src/services/error.js | 3 ++- test/base-commands/base-command.test.js | 13 +++++++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/base-commands/base-command.js b/src/base-commands/base-command.js index db98157b..38de5f9b 100644 --- a/src/base-commands/base-command.js +++ b/src/base-commands/base-command.js @@ -62,8 +62,12 @@ class BaseCommand extends Command { if (instanceOf(error, TwilioCliError)) { // User/API errors - this.logger.error(error.message); - this.logger.debug(error.stack); + if (this.flags['cli-output-format'] === 'json') { + this.output(error); + } else { + this.logger.error(error.message); + this.logger.debug(error.stack); + } this.exit(error.exitCode || 1); } else { // System errors diff --git a/src/services/cli-http-client.js b/src/services/cli-http-client.js index fcac0aba..bdc9b5d1 100644 --- a/src/services/cli-http-client.js +++ b/src/services/cli-http-client.js @@ -94,11 +94,8 @@ class CliRequestClient { this.logger.debug(`response.headers: ${JSON.stringify(response.headers)}`); if (response.status < 200 || response.status >= 400) { - const parsed = response.data; - throw new TwilioCliError( - `Error code ${parsed.code} from Twilio: ${parsed.message}. See ${parsed.more_info} for more info.`, - parsed.code, - ); + const { message, code, details } = this.formatErrorMessage(response.data); + throw new TwilioCliError(message, code, details); } return { @@ -140,6 +137,20 @@ class CliRequestClient { this.logger.debug(`User-Agent: ${options.headers['User-Agent']}`); this.logger.debug('-- END Twilio API Request --'); } + + /* eslint-disable camelcase */ + // In the rare event parameters are missing, display a readable message + formatErrorMessage({ code, message, more_info, details }) { + const moreInfoMessage = more_info ? `See ${more_info} for more info.` : ''; + const error = { + message: `Error code ${code || 'N/A'} from Twilio: ${message || 'No message provided'}. ${moreInfoMessage}`, + code, + details, + }; + + return error; + } + /* eslint-enable camelcase */ } module.exports = CliRequestClient; diff --git a/src/services/error.js b/src/services/error.js index 67342f62..bdf3eb88 100644 --- a/src/services/error.js +++ b/src/services/error.js @@ -1,8 +1,9 @@ class TwilioCliError extends Error { - constructor(message, exitCode) { + constructor(message, exitCode, details = 'No details provided') { super(message); this.name = this.constructor.name; this.exitCode = exitCode; + this.details = details; } } diff --git a/test/base-commands/base-command.test.js b/test/base-commands/base-command.test.js index abd0c860..f65294ee 100644 --- a/test/base-commands/base-command.test.js +++ b/test/base-commands/base-command.test.js @@ -59,6 +59,19 @@ describe('base-commands', () => { expect(ctx.stderr).to.contain('oy!'); }); + test + .twilioCliEnv() + .stdout() + .do(async (ctx) => { + ctx.testCmd = new BaseCommand(['-o', 'json'], ctx.fakeConfig); + await ctx.testCmd.run(); + await ctx.testCmd.catch(new TwilioCliError('oh no', 1, { errors: [{ message: 'oh no' }] })); + }) + .exit(1) + .it('can correctly display error details', (ctx) => { + expect(ctx.stdout).to.contain(`"message": "oh no"`); + }); + test .twilioCliEnv() .do((ctx) => {