diff --git a/src/commands/email/send.js b/src/commands/email/send.js index 8851b3265..bd256d015 100644 --- a/src/commands/email/send.js +++ b/src/commands/email/send.js @@ -2,9 +2,8 @@ const { flags } = require('@oclif/command'); const { BaseCommand } = require('@twilio/cli-core').baseCommands; const { TwilioCliError } = require('@twilio/cli-core').services.error; const emailUtilities = require('../../services/email-utility'); +const { readFileOrStdIn, readFile } = require('../../services/file-io'); const sgMail = require('@sendgrid/mail'); -const fs = require('fs'); -const path = require('path'); class Send extends BaseCommand { async run() { @@ -17,19 +16,12 @@ class Send extends BaseCommand { ); } - this.isTTY = process.stdin.isTTY || this.flags['force-tty']; - - const pipedInput = await this.readStream(); - if (pipedInput) { - this.processData(pipedInput); - } - await this.promptForFromEmail(); await this.promptForToEmail(); await this.promptForSubject(); await this.promptForText(); - if (!this.isTTY && (!this.flags.to || !this.flags.text || !this.subjectLine || !this.fromEmail)) { + if (!process.stdin.isTTY && (!this.flags.to || !this.flags.text || !this.subjectLine || !this.fromEmail)) { throw new TwilioCliError('No terminal input available. Please provide --to, --from, --subject, and --text'); } @@ -44,58 +36,31 @@ class Send extends BaseCommand { html: '

' + this.emailText + '

' }; - if (this.pipedInfo) { - const attachment = this.createAttachmentArray(this.pipedInfo); - sendInformation.attachments = attachment; + const fileInfo = await readFileOrStdIn(this.flags.attachment); + + if (fileInfo) { + sendInformation.attachments = this.createAttachmentArray(fileInfo); } else { const attachmentVerdict = await this.askAttachment(); - await this.promptAttachment(attachmentVerdict); - if (this.attachment) { - const fileContent = this.readFile(this.attachment); - const attachment = this.createAttachmentArray(fileContent); - sendInformation.attachments = attachment; + const attachment = await this.promptAttachment(attachmentVerdict); + + if (attachment) { + sendInformation.attachments = this.createAttachmentArray(readFile(attachment)); } } await this.sendEmail(sendInformation); } - async readStream() { - const input = await this.getStdin(); - return Buffer.from(input).toString('base64'); - } - - getStdin() { - return new Promise(resolve => { - if (this.isTTY) { - resolve(''); - } else { - process.stdin.setEncoding('utf8'); - process.stdin.once('data', data => { - resolve(data); - }); - } - }); - } - - processData(input) { - this.fileName = 'piped.txt'; // placeholder filename for attachement - this.pipedInfo = input; - } - async askAttachment() { - this.attachment = this.flags.attachment; - if (!this.attachment) { - const verdict = await this.inquirer.prompt([ - { - type: 'confirm', - name: 'sendAttachment', - message: 'Would you like to send an attachment?', - default: false - } - ]); - return verdict.sendAttachment; - } - return false; + const verdict = await this.inquirer.prompt([ + { + type: 'confirm', + name: 'sendAttachment', + message: 'Would you like to send an attachment?', + default: false + } + ]); + return verdict.sendAttachment; } async promptAttachment(verdict) { @@ -106,28 +71,17 @@ class Send extends BaseCommand { message: this.getPromptMessage(Send.flags.attachment.description) } ]); - this.attachment = file.path; + return file.path; } } - readFile(filePath) { - this.fileName = path.basename(filePath); - try { - const coded = fs.readFileSync(filePath, 'base64'); - return coded; - } catch (error) { - this.logger.debug(error); - throw new TwilioCliError('Unable to read the file: ' + filePath); - } - } - - createAttachmentArray(fileContent) { + createAttachmentArray(fileInfo) { return [ { - content: fileContent, + content: fileInfo.content, + filename: fileInfo.filename, type: 'plain/text', disposition: 'attachment', - filename: this.fileName, contentId: 'attachmentText' } ]; @@ -135,7 +89,7 @@ class Send extends BaseCommand { validateEmail(email) { let emailList = []; - let validEmail; + let validEmail = true; const multipleEmail = email.includes(','); if (multipleEmail === true) { emailList = email.split(',').map(item => { @@ -166,7 +120,7 @@ class Send extends BaseCommand { this[key] = this.flags[flagKey]; } - if (this.isTTY && !this[key]) { + if (process.stdin.isTTY && !this[key]) { const answer = await this.inquirer.prompt([ { name: flagKey, @@ -196,23 +150,18 @@ class Send extends BaseCommand { async sendEmail(sendInformation) { sgMail.setApiKey(process.env.SENDGRID_API_KEY); await sgMail.send(sendInformation); - this.logger.info( - 'Your email containing the message "' + - this.emailText + - '" sent from ' + - this.fromEmail + - ' to ' + - this.toEmail + - ' with the subject line ' + - this.subjectLine + - ' has been sent!' - ); - if (this.attachment) { - this.logger.info('Your attachment from ' + this.attachment + ' path called ' + this.fileName + ' has been sent.'); - } - if (this.pipedInfo) { - this.logger.info('Your piped data attachment has been sent.'); + + const messageParts = [ + `Your email containing the message "${sendInformation.text}"`, + `sent from "${sendInformation.from}" to "${sendInformation.to}"`, + `with the subject line "${sendInformation.subject}"` + ]; + if (sendInformation.attachments) { + messageParts.push(`with attachment "${sendInformation.attachments[0].filename}"`); } + messageParts.push('has been sent!'); + + this.logger.info(messageParts.join(' ')); } } @@ -233,10 +182,6 @@ Send.flags = Object.assign( }), attachment: flags.string({ description: 'Path for the file that you want to attach.' - }), - 'force-tty': flags.boolean({ - default: false, - hidden: true }) }, BaseCommand.flags diff --git a/src/services/file-io.js b/src/services/file-io.js new file mode 100644 index 000000000..a123604dc --- /dev/null +++ b/src/services/file-io.js @@ -0,0 +1,51 @@ +const { TwilioCliError } = require('@twilio/cli-core').services.error; +const { logger } = require('@twilio/cli-core').services.logging; +const fs = require('fs'); +const path = require('path'); + +async function readFileOrStdIn(filePath) { + if (filePath) { + return readFile(filePath); + } + + const pipedInput = await readStream(); + if (pipedInput) { + return { + filename: 'piped.txt', // placeholder filename for attachment + content: pipedInput + }; + } +} + +function readFile(filePath) { + try { + return { + filename: path.basename(filePath), + content: fs.readFileSync(filePath, 'base64') + }; + } catch (error) { + logger.debug(error); + throw new TwilioCliError('Unable to read the file: ' + filePath); + } +} + +async function readStream() { + const input = await getStdin(); + return Buffer.from(input).toString('base64'); +} + +function getStdin() { + return new Promise(resolve => { + if (process.stdin.isTTY) { + resolve(''); + } else { + process.stdin.setEncoding('utf8'); + process.stdin.once('data', data => resolve(data)); + } + }); +} + +module.exports = { + readFileOrStdIn, + readFile +}; diff --git a/test/.mocharc.yml b/test/.mocharc.yml index 284d7f508..066a84252 100644 --- a/test/.mocharc.yml +++ b/test/.mocharc.yml @@ -1,4 +1,4 @@ require: test/helpers/init.js recursive: true reporter: spec -timeout: 10000 +timeout: false diff --git a/test/commands/email/send.test.js b/test/commands/email/send.test.js index 2a24b5f12..7b82772c0 100644 --- a/test/commands/email/send.test.js +++ b/test/commands/email/send.test.js @@ -6,6 +6,8 @@ const emailSend = require('../../../src/commands/email/send'); describe('commands', () => { describe('email', () => { describe('send', () => { + process.stdin.isTTY = true; + const defaultSetup = ({ flags = [], toEmail = '', @@ -70,7 +72,7 @@ describe('commands', () => { noDefault({ toEmail: 'JonSnow, BranStark@winterfell', fromEmail: 'Ygritte@wall.com', - flags: ['--subject', 'Open ASAP', '--force-tty'], + flags: ['--subject', 'Open ASAP'], bodyText: 'You know nothing Jon Snow.' }) .do(ctx => ctx.testCmd.run()) @@ -86,7 +88,6 @@ describe('commands', () => { toEmail: 'JonSnow@castleBlack.com', fromEmail: 'Ygritte@wall.com', subjectLine: 'Secret Message', - flags: ['--force-tty'], bodyText: 'You know nothing Jon Snow.' }) .nock('https://api.sendgrid.com', api => { @@ -103,7 +104,7 @@ describe('commands', () => { noDefault({ toEmail: 'JonSnow@castleBlack.com', fromEmail: 'Ygritte@wall.com', - flags: ['--subject', 'Open ASAP', '--force-tty'], + flags: ['--subject', 'Open ASAP'], bodyText: 'You know nothing Jon Snow.' }) .nock('https://api.sendgrid.com', api => { @@ -121,7 +122,6 @@ describe('commands', () => { toEmail: 'JonSnow@castleBlack.com', fromEmail: 'Ygritte', subjectLine: 'Secret Message', - flags: ['--force-tty'], bodyText: 'You know nothing Jon Snow.' }) .do(ctx => ctx.testCmd.run()) @@ -130,7 +130,7 @@ describe('commands', () => { expect(ctx.stderr).to.contain('Ygritte is not a valid email'); }); - defaultSetup({ toEmail: 'jen@test.com', flags: ['--force-tty'] }) + defaultSetup({ toEmail: 'jen@test.com' }) .nock('https://api.sendgrid.com', api => { api.post('/v3/mail/send').reply(200, {}); }) @@ -145,7 +145,7 @@ describe('commands', () => { expect(ctx.stderr).to.contain('default'); }); - defaultSetup({ toEmail: 'jen@test.com, mike@test.com, tamu@test.com', flags: ['--force-tty'] }) + defaultSetup({ toEmail: 'jen@test.com, mike@test.com, tamu@test.com' }) .nock('https://api.sendgrid.com', api => { api.post('/v3/mail/send').reply(200, {}); }) @@ -171,8 +171,7 @@ describe('commands', () => { '--subject', 'Greetings', '--text', - 'Short cuts make delays, but inns make longer ones.', - '--force-tty' + 'Short cuts make delays, but inns make longer ones.' ] }) .nock('https://api.sendgrid.com', api => { @@ -196,8 +195,7 @@ describe('commands', () => { '--from', 'Bilbo@test.com', '--text', - 'Short cuts make delays, but inns make longer ones.', - '--force-tty' + 'Short cuts make delays, but inns make longer ones.' ] }) .nock('https://api.sendgrid.com', api => { @@ -225,8 +223,7 @@ describe('commands', () => { '--text', 'You know nothing Jon Snow.', '--attachment', - 'test/commands/email/test.txt', - '--force-tty' + 'test/commands/email/test.txt' ] }) .nock('https://api.sendgrid.com', api => { @@ -241,7 +238,7 @@ describe('commands', () => { expect(ctx.stderr).to.contain('Ygritte@wall.com'); expect(ctx.stderr).to.contain('JonSnow@castleBlack.com'); expect(ctx.stderr).to.contain('Secret Message'); - expect(ctx.stderr).to.contain('test.txt path'); + expect(ctx.stderr).to.contain('test.txt'); }); defaultSetup({ @@ -255,8 +252,7 @@ describe('commands', () => { '--text', 'You know nothing Jon Snow.', '--attachment', - 'test/commands/email/invalid.txt', - '--force-tty' + 'test/commands/email/invalid.txt' ] }) .do(ctx => { @@ -266,7 +262,7 @@ describe('commands', () => { .catch(/Unable to read the file/) .it('run email:send using flags to set information using invalid file path'); - defaultSetup({ toEmail: 'jen@test.com', attachmentVerdict: true, flags: ['--force-tty'] }) + defaultSetup({ toEmail: 'jen@test.com', attachmentVerdict: true }) .nock('https://api.sendgrid.com', api => { api.post('/v3/mail/send').reply(200, {}); }) @@ -278,10 +274,10 @@ describe('commands', () => { 'run email:send with default subject line and sending email address and relative path for attachment', ctx => { expect(ctx.stderr).to.contain('Hello world'); - expect(ctx.stderr).to.contain('default@test.com '); - expect(ctx.stderr).to.contain('jen@test.com '); expect(ctx.stderr).to.contain('default@test.com'); - expect(ctx.stderr).to.contain('test.txt path'); + expect(ctx.stderr).to.contain('jen@test.com'); + expect(ctx.stderr).to.contain('"default"'); + expect(ctx.stderr).to.contain('test.txt'); } ); @@ -310,7 +306,7 @@ describe('commands', () => { expect(ctx.stderr).to.contain('Ygritte@wall.com'); expect(ctx.stderr).to.contain('JonSnow@castleBlack.com'); expect(ctx.stderr).to.contain('Secret Message'); - expect(ctx.stderr).to.contain('Your piped data attachment has been sent'); + expect(ctx.stderr).to.contain('piped'); }); defaultSetup({