From 4bc83e63c0f7196b2980e1d88048c814417a6c39 Mon Sep 17 00:00:00 2001 From: gdereese Date: Tue, 27 Feb 2018 20:25:15 -0600 Subject: [PATCH] closes #11 --- README.md | 9 +-- src/operation-command-action.ts | 84 +++++++++++++++++--------- src/operation-command-builder.ts | 8 +++ test/operation-command-builder.spec.ts | 27 +++++++++ 4 files changed, 95 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 213ac35..5ad17ef 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,11 @@ If the operation has a body parameter, the `body` parameter on the command is us Each API operation command has the following options to configure additional aspects of the request made by the command: -| Option | Description | -| :------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--request-content-type` | Indicates the content type of the request body. Available options are taken from those listed in the operation spec's `consumes` object. | -| `--response-content-type` | Indicates the desired content type of the response to be returned. Available options are taken from those listed in the operation spec's `produces` object. | +| Option | Description | +| :------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--request-content-type` | Indicates the content type of the request body. Available options are taken from those listed in the operation spec's `consumes` object. | +| `--response-content-type` | Indicates the desired content type of the response to be returned. Available options are taken from those listed in the operation spec's `produces` object. | +| `--to-file` | Writes the operation's response body to a file. This option is only available if the operation specifies one or more response content types in the `produces` object. | ### Global Commands diff --git a/src/operation-command-action.ts b/src/operation-command-action.ts index 6227678..da29f39 100644 --- a/src/operation-command-action.ts +++ b/src/operation-command-action.ts @@ -1,4 +1,5 @@ import { Spinner } from 'cli-spinner'; +import * as fs from 'fs'; import * as _ from 'lodash'; import { ApiExecuteOptionsFactory } from './api-execute-options-factory'; @@ -34,44 +35,69 @@ export class OperationCommandAction { // TODO: change to return response string (instead of writing to log); perhaps commands could then be piped? return client .execute(executeOptions) - .then(response => - self.handleResponse( + .then(response => { + return self.handleResponse( response, spinner, - this.command.parent.chalk.green - ) - ) - .catch(error => - self.handleResponse( + this.command.parent.chalk.green, + args.options['to-file'] + ); + }) + .catch(error => { + return self.handleResponse( error.response, spinner, - this.command.parent.chalk.red - ) - ); + this.command.parent.chalk.red, + args.options['to-file'] + ); + }); }); } - private handleResponse(response, spinner: Spinner, chalkColor) { - spinner.stop(true); + private handleResponse( + response, + spinner: Spinner, + chalkColor, + bodyPath: string + ): Promise { + return new Promise((resolve, reject) => { + spinner.stop(true); - let result = response.status + ' ' + response.statusText; - // if response is expected per the operation spec, display the response description - const responseSpec = this.commandInfo.operation.responses[response.status]; - if (responseSpec && responseSpec.description) { - result += ': ' + responseSpec.description; - } - this.command.log(chalkColor(result)); + let result = response.status + ' ' + response.statusText; + // if response is expected per the operation spec, display the response description + const responseSpec = this.commandInfo.operation.responses[ + response.status + ]; + if (responseSpec && responseSpec.description) { + result += ': ' + responseSpec.description; + } + this.command.log(chalkColor(result)); - let responseString: string = null; - if (typeof response.data === 'string') { - responseString = response.data; - } else if (response.data instanceof Buffer) { - responseString = (response.data as Buffer).toString(); - } - if (responseString && responseString.length > 0) { - this.command.log(responseString); - } + let responseString: string = null; + if (typeof response.data === 'string') { + responseString = response.data; + } else if (response.data instanceof Buffer) { + responseString = (response.data as Buffer).toString(); + } + if (responseString && responseString.length > 0) { + this.command.log(responseString); + } - this.command.log(); + this.command.log(); + + // if body need to be written to file, resolve promise using writeFile callback; + // otherwise, resolve it now + if (bodyPath) { + fs.writeFile(bodyPath, responseString, err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + } else { + resolve(); + } + }); } } diff --git a/src/operation-command-builder.ts b/src/operation-command-builder.ts index c0d07e9..02176be 100644 --- a/src/operation-command-builder.ts +++ b/src/operation-command-builder.ts @@ -27,6 +27,14 @@ export class OperationCommandBuilder { const command = vorpal.command(commandString, commandDescription); + // add option for writing response body to file if operation produces body content + if ( + commandInfo.operation.produces && + commandInfo.operation.produces.length > 0 + ) { + command.option('--to-file ', 'file path to write response body'); + } + // add option for request content type (based on values in consumes array) if ( commandInfo.operation.consumes && diff --git a/test/operation-command-builder.spec.ts b/test/operation-command-builder.spec.ts index 7354dbe..9819901 100644 --- a/test/operation-command-builder.spec.ts +++ b/test/operation-command-builder.spec.ts @@ -34,6 +34,33 @@ describe('operation-command-builder', () => { expect(option.autocomplete).toContain(commandInfo.operation.consumes[1]); }); + it('adds option for response body output if operation specifies produces values', () => { + const vorpalInstance = vorpal(); + const options = { + interactive: false, + operations: { + groupBy: null + }, + spec: {} + }; + + const builder = new OperationCommandBuilder(); + + const commandInfo = { + commandStringParts: [], + operation: { + produces: ['foo', 'bar'] + }, + pathKey: null + }; + + const command = builder.build(vorpalInstance, options, commandInfo); + + const option = _.find(command.options, { long: '--to-file' }); + + expect(option).toBeTruthy(); + }); + it('adds option for response content type if operation specifies produces values', () => { const vorpalInstance = vorpal(); const options = {