From d00bc6f7c6df6b4dce7536dc0e5def7854f172b4 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 16 Jun 2020 11:24:45 +1000 Subject: [PATCH 1/7] fix(printers): don't write deploy config to stdout --- src/printers/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/printers/deploy.ts b/src/printers/deploy.ts index d8dd139a..e00dc6b9 100644 --- a/src/printers/deploy.ts +++ b/src/printers/deploy.ts @@ -83,7 +83,7 @@ function prettyPrintConfigInfo(config: DeployLocalProjectConfig) { } logger.info('\nDeploying functions & assets to the Twilio Runtime'); - writeOutput( + logger.info( chalk` {bold.cyan Account}\t\t${config.accountSid} {bold.cyan Token}\t\t${redactPartOfString(config.authToken)} From 630bbf51b6b1e4d2072b1cf9428caed00ba5fc69 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 16 Jun 2020 11:49:57 +1000 Subject: [PATCH 2/7] feat(cli): adds option to print JSON output --- src/commands/activate.ts | 5 +++-- src/commands/shared.ts | 10 ++++++++++ src/printers/activate.ts | 21 ++++++++++++++++++--- src/utils/output.ts | 4 ++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/commands/activate.ts b/src/commands/activate.ts index 4b1a0053..1a4ae308 100644 --- a/src/commands/activate.ts +++ b/src/commands/activate.ts @@ -44,6 +44,7 @@ export async function handler( flags: ActivateCliFlags, externalCliOptions?: ExternalCliOptions ): Promise { + const outputFormat = flags.output; setLogLevelByName(flags.logLevel); let config: ActivateConfig; try { @@ -63,7 +64,7 @@ export async function handler( checkConfigForCredentials(config); - printActivateConfig(config); + printActivateConfig(config, outputFormat); const details = config.buildSid ? `(${config.buildSid})` @@ -78,7 +79,7 @@ export async function handler( `Activated new build ${details} on ${config.targetEnvironment || 'production'}` ); - printActivateResult(result); + printActivateResult(result, outputFormat); } catch (err) { handleError(err, spinner); } diff --git a/src/commands/shared.ts b/src/commands/shared.ts index dd8cf7da..037fd2de 100644 --- a/src/commands/shared.ts +++ b/src/commands/shared.ts @@ -1,8 +1,11 @@ import { Options } from 'yargs'; import { LoggingLevel, LoggingLevelNames } from '../utils/logger'; +export type OutputFormat = 'json' | 'pretty' | undefined; + export type BaseFlags = { logLevel: LoggingLevelNames; + output: OutputFormat; }; export type SharedFlags = BaseFlags & { @@ -36,6 +39,13 @@ export const baseCliOptions: { [key: string]: Options } = { describe: 'Level of logging messages.', choices: Object.keys(LoggingLevel), }, + output: { + type: 'string', + default: 'pretty', + alias: 'o', + describe: 'Format of command output.', + choices: ['json', 'pretty'], + }, }; export const sharedApiRelatedCliOptions: { [key: string]: Options } = { diff --git a/src/printers/activate.ts b/src/printers/activate.ts index c211994a..ac9948d9 100644 --- a/src/printers/activate.ts +++ b/src/printers/activate.ts @@ -1,12 +1,19 @@ import { ActivateConfig, ActivateResult } from '@twilio-labs/serverless-api'; import { stripIndent } from 'common-tags'; import { logger } from '../utils/logger'; -import { writeOutput } from '../utils/output'; +import { writeOutput, writeJSONOutput } from '../utils/output'; import { getTwilioConsoleDeploymentUrl, redactPartOfString } from './utils'; import chalk = require('chalk'); import terminalLink = require('terminal-link'); +import { OutputFormat } from '../commands/shared'; -export function printActivateConfig(config: ActivateConfig) { +export function printActivateConfig( + config: ActivateConfig, + outputFormat: OutputFormat +) { + if (outputFormat === 'json') { + return; + } const message = chalk` {cyan.bold Account} ${config.accountSid} {cyan.bold Token} ${redactPartOfString(config.authToken)} @@ -14,7 +21,15 @@ export function printActivateConfig(config: ActivateConfig) { logger.info(stripIndent(message) + '\n'); } -export function printActivateResult(result: ActivateResult) { +export function printActivateResult( + result: ActivateResult, + outputFormat: OutputFormat +) { + if (outputFormat === 'json') { + writeJSONOutput(result, null, '\t'); + return; + } + logger.info(chalk.cyan.bold('\nActive build available at:')); writeOutput(result.domain); diff --git a/src/utils/output.ts b/src/utils/output.ts index 113eb6a6..b10db1a0 100644 --- a/src/utils/output.ts +++ b/src/utils/output.ts @@ -1,3 +1,7 @@ export function writeOutput(...args: any[]) { console.log(...args); } + +export function writeJSONOutput(...args: any[]) { + writeOutput(JSON.stringify(args, null, '\t')); +} From 24fe66db8680541275d74859c413e86f088067af Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 16 Jun 2020 12:10:16 +1000 Subject: [PATCH 3/7] feat(cli): adds output format to deploy command --- src/commands/deploy.ts | 6 +++--- src/printers/deploy.ts | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/commands/deploy.ts b/src/commands/deploy.ts index cc152b32..266d106e 100644 --- a/src/commands/deploy.ts +++ b/src/commands/deploy.ts @@ -74,7 +74,7 @@ export async function handler( externalCliOptions?: ExternalCliOptions ): Promise { setLogLevelByName(flags.logLevel); - + const outputFormat = flags.output; const cwd = flags.cwd ? path.resolve(flags.cwd) : process.cwd(); flags.cwd = cwd; const command = getFullCommand(flags); @@ -100,7 +100,7 @@ export async function handler( checkConfigForCredentials(config); - printConfigInfo(config); + printConfigInfo(config, outputFormat); const spinner = getOraSpinner('Deploying Function').start(); try { @@ -111,7 +111,7 @@ export async function handler( const result = await client.deployLocalProject(config); spinner.text = 'Serverless project successfully deployed\n'; spinner.succeed(); - printDeployedResources(config, result); + printDeployedResources(config, result, outputFormat); const { serviceSid, buildSid } = result; await saveLatestDeploymentData( config.cwd, diff --git a/src/printers/deploy.ts b/src/printers/deploy.ts index e00dc6b9..c361eb7c 100644 --- a/src/printers/deploy.ts +++ b/src/printers/deploy.ts @@ -10,13 +10,14 @@ import { stripIndent } from 'common-tags'; import terminalLink from 'terminal-link'; import { MergeExclusive } from 'type-fest'; import { logger } from '../utils/logger'; -import { writeOutput } from '../utils/output'; +import { writeOutput, writeJSONOutput } from '../utils/output'; import { getTwilioConsoleDeploymentUrl, printObjectWithoutHeaders, redactPartOfString, shouldPrettyPrint, } from './utils'; +import { OutputFormat } from '../commands/shared'; function sortByAccess< T extends MergeExclusive @@ -171,7 +172,13 @@ function prettyPrintDeployedResources( } } -export function printConfigInfo(config: DeployLocalProjectConfig) { +export function printConfigInfo( + config: DeployLocalProjectConfig, + outputFormat: OutputFormat +) { + if (outputFormat === 'json') { + return; + } if (shouldPrettyPrint) { prettyPrintConfigInfo(config); } else { @@ -181,8 +188,13 @@ export function printConfigInfo(config: DeployLocalProjectConfig) { export function printDeployedResources( config: DeployLocalProjectConfig, - result: DeployResult + result: DeployResult, + outputFormat: OutputFormat ) { + if (outputFormat === 'json') { + writeJSONOutput(result); + return; + } if (shouldPrettyPrint) { prettyPrintDeployedResources(config, result); } else { From 445ac90069705a6bf74b8ac67adef0c922a93687 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 18 Jun 2020 11:05:11 +1000 Subject: [PATCH 4/7] feat(cli): JSON output for list command --- src/commands/list.ts | 4 ++-- src/printers/list.ts | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/commands/list.ts b/src/commands/list.ts index 875c5d7b..efe16515 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -39,7 +39,7 @@ export async function handler( externalCliOptions?: ExternalCliOptions ): Promise { setLogLevelByName(flags.logLevel); - + const outputFormat = flags.output; let config: ListConfig; try { config = await getConfigFromFlags(flags, externalCliOptions); @@ -66,7 +66,7 @@ export async function handler( try { const client = new TwilioServerlessApiClient(config); const result = await client.list({ ...config }); - printListResult(result, config); + printListResult(result, config, outputFormat); } catch (err) { handleError(err); } diff --git a/src/printers/list.ts b/src/printers/list.ts index bce8ffd4..cd439c7a 100644 --- a/src/printers/list.ts +++ b/src/printers/list.ts @@ -16,8 +16,9 @@ import logSymbols from 'log-symbols'; import title from 'title'; import { ListConfig } from '../config/list'; import { logger } from '../utils/logger'; -import { writeOutput } from '../utils/output'; +import { writeOutput, writeJSONOutput } from '../utils/output'; import { redactPartOfString, shouldPrettyPrint, windowSize } from './utils'; +import { OutputFormat } from '../commands/shared'; type KeyMaps = { [key in ListOptions]: string[]; @@ -313,7 +314,15 @@ function printListResultTerminal(result: ListResult, config: ListConfig): void { writeOutput(output); } -export function printListResult(result: ListResult, config: ListConfig): void { +export function printListResult( + result: ListResult, + config: ListConfig, + outputFormat: OutputFormat +): void { + if (outputFormat === 'json') { + writeJSONOutput(result); + return; + } if (shouldPrettyPrint && !config.properties && !config.extendedOutput) { printListResultTerminal(result, config); } else { From 993dd73e38f64213940c26f1a49250df9c96f33f Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 18 Jun 2020 11:20:48 +1000 Subject: [PATCH 5/7] feat(cli): extracts printing from list-templates command Adds output format selection for list-templates. Doesn't pretty print templates if it can't. --- src/commands/list-templates.ts | 10 +++------- src/printers/list-templates.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 src/printers/list-templates.ts diff --git a/src/commands/list-templates.ts b/src/commands/list-templates.ts index b15855e3..a8137fa8 100644 --- a/src/commands/list-templates.ts +++ b/src/commands/list-templates.ts @@ -1,13 +1,13 @@ -import chalk from 'chalk'; import { Arguments } from 'yargs'; import { fetchListOfTemplates } from '../templating/actions'; import { getOraSpinner, setLogLevelByName } from '../utils/logger'; -import { writeOutput } from '../utils/output'; import { baseCliOptions, BaseFlags } from './shared'; import { CliInfo } from './types'; +import { printTemplates } from '../printers/list-templates'; export async function handler(flags: Arguments): Promise { setLogLevelByName(flags.logLevel); + const outputFormat = flags.output; const spinner = getOraSpinner('Fetching available templates').start(); let templates; @@ -21,11 +21,7 @@ export async function handler(flags: Arguments): Promise { spinner.stop(); - templates.forEach(template => { - writeOutput( - chalk`‣ ${template.name} ({cyan ${template.id}})\n {dim ${template.description}}` - ); - }); + printTemplates(templates, outputFormat); } export const cliInfo: CliInfo = { options: { ...baseCliOptions } }; diff --git a/src/printers/list-templates.ts b/src/printers/list-templates.ts new file mode 100644 index 00000000..6bc2cd3f --- /dev/null +++ b/src/printers/list-templates.ts @@ -0,0 +1,34 @@ +import chalk from 'chalk'; +import { Template } from '../templating/data'; +import { writeOutput, writeJSONOutput } from '../utils/output'; +import { OutputFormat } from '../commands/shared'; +import { shouldPrettyPrint } from './utils'; + +function prettyPrintTemplates(templates: Template[]) { + templates.forEach(template => { + writeOutput( + chalk`‣ ${template.name} ({cyan ${template.id}})\n {dim ${template.description}}` + ); + }); +} + +function plainPrintTemplates(templates: Template[]) { + templates.forEach(template => { + writeOutput(`${template.name} (${template.id})\n ${template.description}`); + }); +} + +export function printTemplates( + templates: Template[], + outputFormat: OutputFormat +) { + if (outputFormat === 'json') { + writeJSONOutput(templates); + return; + } + if (shouldPrettyPrint) { + prettyPrintTemplates(templates); + } else { + plainPrintTemplates(templates); + } +} From a5cb12a81e27159e4554325d727701c85f5670c6 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 18 Jun 2020 12:18:50 +1000 Subject: [PATCH 6/7] feat(cli): adjusted logs flags to use base output-format flag Also, updated the flag to be output-format instead of output, which made more sense. --- src/commands/activate.ts | 5 ++--- src/commands/deploy.ts | 5 ++--- src/commands/list-templates.ts | 3 +-- src/commands/list.ts | 3 +-- src/commands/logs.ts | 12 ++---------- src/commands/shared.ts | 4 ++-- src/config/logs.ts | 4 ---- src/printers/logs.ts | 6 +++--- 8 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/commands/activate.ts b/src/commands/activate.ts index 1a4ae308..b59780ec 100644 --- a/src/commands/activate.ts +++ b/src/commands/activate.ts @@ -44,7 +44,6 @@ export async function handler( flags: ActivateCliFlags, externalCliOptions?: ExternalCliOptions ): Promise { - const outputFormat = flags.output; setLogLevelByName(flags.logLevel); let config: ActivateConfig; try { @@ -64,7 +63,7 @@ export async function handler( checkConfigForCredentials(config); - printActivateConfig(config, outputFormat); + printActivateConfig(config, flags.outputFormat); const details = config.buildSid ? `(${config.buildSid})` @@ -79,7 +78,7 @@ export async function handler( `Activated new build ${details} on ${config.targetEnvironment || 'production'}` ); - printActivateResult(result, outputFormat); + printActivateResult(result, flags.outputFormat); } catch (err) { handleError(err, spinner); } diff --git a/src/commands/deploy.ts b/src/commands/deploy.ts index 266d106e..a8b4b787 100644 --- a/src/commands/deploy.ts +++ b/src/commands/deploy.ts @@ -74,7 +74,6 @@ export async function handler( externalCliOptions?: ExternalCliOptions ): Promise { setLogLevelByName(flags.logLevel); - const outputFormat = flags.output; const cwd = flags.cwd ? path.resolve(flags.cwd) : process.cwd(); flags.cwd = cwd; const command = getFullCommand(flags); @@ -100,7 +99,7 @@ export async function handler( checkConfigForCredentials(config); - printConfigInfo(config, outputFormat); + printConfigInfo(config, flags.outputFormat); const spinner = getOraSpinner('Deploying Function').start(); try { @@ -111,7 +110,7 @@ export async function handler( const result = await client.deployLocalProject(config); spinner.text = 'Serverless project successfully deployed\n'; spinner.succeed(); - printDeployedResources(config, result, outputFormat); + printDeployedResources(config, result, flags.outputFormat); const { serviceSid, buildSid } = result; await saveLatestDeploymentData( config.cwd, diff --git a/src/commands/list-templates.ts b/src/commands/list-templates.ts index a8137fa8..ee421c70 100644 --- a/src/commands/list-templates.ts +++ b/src/commands/list-templates.ts @@ -7,7 +7,6 @@ import { printTemplates } from '../printers/list-templates'; export async function handler(flags: Arguments): Promise { setLogLevelByName(flags.logLevel); - const outputFormat = flags.output; const spinner = getOraSpinner('Fetching available templates').start(); let templates; @@ -21,7 +20,7 @@ export async function handler(flags: Arguments): Promise { spinner.stop(); - printTemplates(templates, outputFormat); + printTemplates(templates, flags.outputFormat); } export const cliInfo: CliInfo = { options: { ...baseCliOptions } }; diff --git a/src/commands/list.ts b/src/commands/list.ts index efe16515..220d3fe4 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -39,7 +39,6 @@ export async function handler( externalCliOptions?: ExternalCliOptions ): Promise { setLogLevelByName(flags.logLevel); - const outputFormat = flags.output; let config: ListConfig; try { config = await getConfigFromFlags(flags, externalCliOptions); @@ -66,7 +65,7 @@ export async function handler( try { const client = new TwilioServerlessApiClient(config); const result = await client.list({ ...config }); - printListResult(result, config, outputFormat); + printListResult(result, config, flags.outputFormat); } catch (err) { handleError(err); } diff --git a/src/commands/logs.ts b/src/commands/logs.ts index dcc0e169..eedcea58 100644 --- a/src/commands/logs.ts +++ b/src/commands/logs.ts @@ -42,7 +42,6 @@ export async function handler( externalCliOptions?: ExternalCliOptions ): Promise { setLogLevelByName(flags.logLevel); - let config: LogsConfig; try { config = await getConfigFromFlags(flags, externalCliOptions); @@ -68,11 +67,11 @@ export async function handler( if (flags.tail) { const stream = await client.getLogsStream({ ...config }); stream.on('data', (log: LogApiResource) => { - printLog(log, config.outputFormat); + printLog(log, flags.outputFormat); }); } else { const result = (await client.getLogs({ ...config })) as LogApiResource[]; - printLogs(result, config, config.outputFormat); + printLogs(result, flags.outputFormat); } } catch (err) { handleError(err); @@ -100,13 +99,6 @@ export const cliInfo: CliInfo = { type: 'boolean', describe: 'Continuously stream the logs', }, - 'output-format': { - type: 'string', - alias: 'o', - default: '', - describe: 'Output the log in a different format', - choices: ['', 'json'], - }, env: { type: 'string', describe: diff --git a/src/commands/shared.ts b/src/commands/shared.ts index 037fd2de..a62845b5 100644 --- a/src/commands/shared.ts +++ b/src/commands/shared.ts @@ -5,7 +5,7 @@ export type OutputFormat = 'json' | 'pretty' | undefined; export type BaseFlags = { logLevel: LoggingLevelNames; - output: OutputFormat; + outputFormat: OutputFormat; }; export type SharedFlags = BaseFlags & { @@ -39,7 +39,7 @@ export const baseCliOptions: { [key: string]: Options } = { describe: 'Level of logging messages.', choices: Object.keys(LoggingLevel), }, - output: { + 'output-format': { type: 'string', default: 'pretty', alias: 'o', diff --git a/src/config/logs.ts b/src/config/logs.ts index 6c880fbd..1aa85d5f 100644 --- a/src/config/logs.ts +++ b/src/config/logs.ts @@ -21,7 +21,6 @@ export type LogsConfig = ClientConfig & accountSid: string; authToken: string; properties?: string[]; - outputFormat?: string; }; export type LogsCliFlags = Arguments< @@ -31,7 +30,6 @@ export type LogsCliFlags = Arguments< serviceSid?: string; functionSid?: string; tail: boolean; - outputFormat?: string; } >; @@ -64,7 +62,6 @@ export async function getConfigFromFlags( const command = getFullCommand(flags); const serviceSid = checkForValidServiceSid(command, flags.serviceSid); - const outputFormat = flags.outputFormat || externalCliOptions?.outputFormat; const region = flags.region; const edge = flags.edge; @@ -74,7 +71,6 @@ export async function getConfigFromFlags( authToken, environment, serviceSid, - outputFormat, filterByFunction: flags.functionSid, tail: flags.tail, region, diff --git a/src/printers/logs.ts b/src/printers/logs.ts index 15bda668..2a56d332 100644 --- a/src/printers/logs.ts +++ b/src/printers/logs.ts @@ -1,16 +1,16 @@ import { LogList, LogApiResource } from '@twilio-labs/serverless-api'; import { LogsConfig } from '../config/logs'; import { writeOutput } from '../utils/output'; +import { OutputFormat } from '../commands/shared'; export function printLogs( result: LogApiResource[], - config: LogsConfig, - outputFormat?: string + outputFormat: OutputFormat ) { result.forEach(log => printLog(log, outputFormat)); } -export function printLog(log: LogApiResource, outputFormat?: string) { +export function printLog(log: LogApiResource, outputFormat: OutputFormat) { if (outputFormat === 'json') { writeOutput(JSON.stringify(log)); } else { From d5fc1aff4f3129e5d18a9d8827b593efcb79d6c5 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Thu, 18 Jun 2020 13:11:33 +1000 Subject: [PATCH 7/7] refactor(new): move printing from downloadTemplates to printer function --- src/commands/new.ts | 2 ++ src/printers/new.ts | 16 ++++++++++++++++ src/templating/actions.ts | 14 -------------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/commands/new.ts b/src/commands/new.ts index 1655a994..9558e3f3 100644 --- a/src/commands/new.ts +++ b/src/commands/new.ts @@ -9,6 +9,7 @@ import { setLogLevelByName, logger } from '../utils/logger'; import { baseCliOptions, BaseFlags, ExternalCliOptions } from './shared'; import { CliInfo } from './types'; import { getFullCommand } from './utils'; +import { printNewResult } from '../printers/new'; export type NewCliFlags = Arguments< BaseFlags & { @@ -114,6 +115,7 @@ export async function handler( try { await downloadTemplate(flags.template, sanitizedNamespace, targetDirectory); + printNewResult(sanitizedNamespace, flags.template); } catch (error) { logger.error(error.message, error.name); } diff --git a/src/printers/new.ts b/src/printers/new.ts index e69de29b..c26ba5f7 100644 --- a/src/printers/new.ts +++ b/src/printers/new.ts @@ -0,0 +1,16 @@ +import chalk from 'chalk'; +import { logger } from '../utils/logger'; +import { join } from 'path'; + +export function printNewResult(namespace: string, templateName: string) { + logger.info( + chalk`{green SUCCESS} Downloaded new template into the "${namespace}" subdirectories.` + ); + logger.info( + `Check ${join( + 'readmes', + namespace, + `${templateName}.md` + )} for template instructions.` + ); +} diff --git a/src/templating/actions.ts b/src/templating/actions.ts index 4cdd03af..bef2fb3d 100644 --- a/src/templating/actions.ts +++ b/src/templating/actions.ts @@ -1,8 +1,5 @@ -import chalk from 'chalk'; -import { logger } from '../utils/logger'; import { getTemplateFiles } from './data'; import { writeFiles } from './filesystem'; -import path from 'path'; export async function downloadTemplate( templateName: string, @@ -10,18 +7,7 @@ export async function downloadTemplate( targetDirectory: string ): Promise { const files = await getTemplateFiles(templateName); - await writeFiles(files, targetDirectory, namespace, templateName); - logger.info( - chalk`{green SUCCESS} Downloaded new template into the "${namespace}" subdirectories.` - ); - logger.info( - `Check ${path.join( - 'readmes', - namespace, - `${templateName}.md` - )} for template instructions.` - ); } export { fetchListOfTemplates } from './data';