Skip to content

Commit

Permalink
feat: add support for Twilio Email (#1093)
Browse files Browse the repository at this point in the history
  • Loading branch information
childish-sambino authored Apr 21, 2020
1 parent 6ef53e5 commit 39ea910
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 82 deletions.
48 changes: 26 additions & 22 deletions docs/use-cases/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
This documentation provides examples for specific Twilio SendGrid v3 API use cases. Please [open an issue](https://github.com/sendgrid/sendgrid-nodejs/issues) or make a pull request for any email use cases you would like us to document here. Thank you!

# Email Use Cases
* [Send a Single Email to a Single Recipient](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/single-email-single-recipient.md)
* [Send a Single Email to Multiple Recipients](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/single-email-multiple-recipients.md)
* [Send Multiple Emails to Multiple Recipients](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/multiple-emails-multiple-recipients.md)
* [CC, BCC and Reply To](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/cc-bcc-reply-to.md)
* [Flexible Email Address Fields](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/flexible-address-fields.md)
* [Handling Success/Failure/Errors](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/success-failure-errors.md)
* [Show Email Activity](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/email-activity.md)
* [Advanced Usage](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/advanced.md)
* [Transactional Templates](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/transactional-templates.md)
* [Legacy Transactional Templates](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/transactional-legacy-templates.md)
* [Hide Warnings](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/hide-warnings.md)
* [Attachments](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/attachments.md)
* [Customization Per Recipient](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/customization.md)
* [Manually Providing Content](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/manual-content.md)
* [Specifying Time to Send At](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/time-to-send.md)
* [Specifying Custom Headers](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/custom-headers.md)
* [Specifying Categories](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/categories.md)
* [Timeout](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/timeout.md)
* [Kitchen Sink - an example with all settings used](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/kitchen-sink.md)
* [Send a Single Email to a Single Recipient](single-email-single-recipient.md)
* [Send a Single Email to Multiple Recipients](single-email-multiple-recipients.md)
* [Send Multiple Emails to Multiple Recipients](multiple-emails-multiple-recipients.md)
* [CC, BCC and Reply To](cc-bcc-reply-to.md)
* [Flexible Email Address Fields](flexible-address-fields.md)
* [Handling Success/Failure/Errors](success-failure-errors.md)
* [Show Email Activity](email-activity.md)
* [Advanced Usage](advanced.md)
* [Transactional Templates](transactional-templates.md)
* [Legacy Transactional Templates](transactional-legacy-templates.md)
* [Hide Warnings](hide-warnings.md)
* [Attachments](attachments.md)
* [Customization Per Recipient](customization.md)
* [Manually Providing Content](manual-content.md)
* [Specifying Time to Send At](time-to-send.md)
* [Specifying Custom Headers](custom-headers.md)
* [Specifying Categories](categories.md)
* [Timeout](timeout.md)
* [Kitchen Sink - an example with all settings used](kitchen-sink.md)

# Twilio Use Cases
* [Twilio Setup](twilio-setup.md)
* [Send an Email With Twilio Email (Pilot)](twilio-email.md)
* [Send an SMS Message](sms.md)

# Non-Email Use Cases
* [Send a SMS Message](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/sms.md)
* [How to Setup a Domain Whitelabel](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/domain-white-label.md)
* [How to View Email Statistics](https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/email-stats.md)
* [How to Set up a Domain Whitelabel](domain-white-label.md)
* [How to View Email Statistics](email-stats.md)
39 changes: 4 additions & 35 deletions docs/use-cases/sms.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,12 @@
Following are the steps to add Twilio SMS to your app:
First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.

## 1. Obtain a Free Twilio Account

Sign up for a free Twilio account [here](https://www.twilio.com/try-twilio?source=sendgrid-nodejs).

## 2. Update Your Environment Variables

You can obtain your Account Sid and Auth Token from [twilio.com/console](https://twilio.com/console).

### Mac
Then, install the Twilio Helper Library.

```bash
echo "export TWILIO_ACCOUNT_SID='YOUR_TWILIO_ACCOUNT_SID'" > twilio.env
echo "export TWILIO_AUTH_TOKEN='YOUR_TWILIO_AUTH_TOKEN'" >> twilio.env
echo "twilio.env" >> .gitignore
source ./twilio.env
npm install twilio
```

### Windows

Temporarily set the environment variable (accessible only during the current CLI session):

```bash
set TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID
set TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN
```

Permanently set the environment variable (accessible in all subsequent CLI sessions):

```bash
setx TWILIO_ACCOUNT_SID "YOUR_TWILIO_ACCOUNT_SID"
setx TWILIO_AUTH_TOKEN "YOUR_TWILIO_AUTH_TOKEN"
```

## 3. Install the Twilio Helper Library

`npm install twilio`

Then, you can execute the following code.
Finally, send a message.

```js
var accountSid = process.env.TWILIO_ACCOUNT_SID;
Expand Down
27 changes: 27 additions & 0 deletions docs/use-cases/twilio-email.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.

Then, initialize the Twilio Email Client.

```js
const client = require('@sendgrid/client');

client.setTwilioEmailAuth(process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET);

// or

client.setTwilioEmailAuth(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
```

Or similarly using the mail helper.

```js
const mail = require('@sendgrid/mail');

mail.setTwilioEmailAuth(process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET);

// or

mail.setTwilioEmailAuth(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
```

This sets the client to use Twilio Auth and the Twilio Email API.
54 changes: 54 additions & 0 deletions docs/use-cases/twilio-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## 1. Obtain a Free Twilio Account

Sign up for a free Twilio account [here](https://www.twilio.com/try-twilio?source=sendgrid-nodejs).

## 2. Set Up Your Environment Variables

The Twilio API allows for authentication using with either an API key/secret or your Account SID/Auth Token. You can create an API key [here](https://twil.io/get-api-key) or obtain your Account SID and Auth Token [here](https://twil.io/console).

Once you have those, follow the steps below based on your operating system.

### Linux/Mac

```bash
echo "export TWILIO_API_KEY='YOUR_TWILIO_API_KEY'" > twilio.env
echo "export TWILIO_API_SECRET='YOUR_TWILIO_API_SECRET'" >> twilio.env

# or

echo "export TWILIO_ACCOUNT_SID='YOUR_TWILIO_ACCOUNT_SID'" > twilio.env
echo "export TWILIO_AUTH_TOKEN='YOUR_TWILIO_AUTH_TOKEN'" >> twilio.env
```

Then:

```bash
echo "twilio.env" >> .gitignore
source ./twilio.env
```

### Windows

Temporarily set the environment variable (accessible only during the current CLI session):

```bash
set TWILIO_API_KEY=YOUR_TWILIO_API_KEY
set TWILIO_API_SECRET=YOUR_TWILIO_API_SECRET

: or

set TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID
set TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN
```

Or permanently set the environment variable (accessible in all subsequent CLI sessions):

```bash
setx TWILIO_API_KEY "YOUR_TWILIO_API_KEY"
setx TWILIO_API_SECRET "YOUR_TWILIO_API_SECRET"

: or

setx TWILIO_ACCOUNT_SID "YOUR_TWILIO_ACCOUNT_SID"
setx TWILIO_AUTH_TOKEN "YOUR_TWILIO_AUTH_TOKEN"
```
3 changes: 3 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"@sendgrid/helpers": "^7.0.1",
"axios": "^0.19.2"
},
"devDependencies": {
"nock": "^10.0.6"
},
"tags": [
"http",
"rest",
Expand Down
30 changes: 24 additions & 6 deletions packages/client/src/classes/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ const {
} = require('@sendgrid/helpers');

const API_KEY_PREFIX = 'SG.';
const SENDGRID_BASE_URL = 'https://api.sendgrid.com/';
const TWILIO_BASE_URL = 'https://email.twilio.com/';

class Client {
constructor() {
this.apiKey = '';
this.auth = '';

this.defaultHeaders = {
Accept: 'application/json',
Expand All @@ -24,25 +26,41 @@ class Client {
};

this.defaultRequest = {
baseUrl: 'https://api.sendgrid.com/',
baseUrl: SENDGRID_BASE_URL,
url: '',
method: 'GET',
headers: {},
};
}

setApiKey(apiKey) {
this.apiKey = apiKey;
this.auth = 'Bearer ' + apiKey;
this.setDefaultRequest('baseUrl', SENDGRID_BASE_URL);

if (!this.isValidApiKey(apiKey)) {
console.warn(`API key does not start with "${API_KEY_PREFIX}".`);
}
}

setTwilioEmailAuth(username, password) {
const b64Auth = Buffer.from(username + ':' + password).toString('base64');
this.auth = 'Basic ' + b64Auth;
this.setDefaultRequest('baseUrl', TWILIO_BASE_URL);

if (!this.isValidTwilioAuth(username, password)) {
console.warn('Twilio Email credentials must be non-empty strings.');
}
}

isValidApiKey(apiKey) {
return this.isString(apiKey) && apiKey.trim().startsWith(API_KEY_PREFIX);
}

isValidTwilioAuth(username, password) {
return this.isString(username) && username
&& this.isString(password) && password;
}

isString(value) {
return typeof value === 'string' || value instanceof String;
}
Expand All @@ -61,9 +79,9 @@ class Client {
// Merge data with default headers.
const headers = mergeData(this.defaultHeaders, data);

// Add API key, but don't overwrite if header already set.
if (typeof headers.Authorization === 'undefined' && this.apiKey) {
headers.Authorization = 'Bearer ' + this.apiKey;
// Add auth, but don't overwrite if header already set.
if (typeof headers.Authorization === 'undefined' && this.auth) {
headers.Authorization = this.auth;
}

return headers;
Expand Down
8 changes: 7 additions & 1 deletion packages/client/src/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import {ClientResponse} from "@sendgrid/client/src/response";

declare class Client {
constructor();

/**
* Set API key
* Set the SendGrid API key.
*/
setApiKey(apiKey: string): void;

/**
* Set the Twilio Email credentials.
*/
setTwilioEmailAuth(username: string, password: string): void;

/**
* Set default header
*/
Expand Down
57 changes: 47 additions & 10 deletions packages/client/src/client.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use strict';
const nock = require('nock');

const baseUrl = 'http://localhost:4010/';

const testRequest = (request, statusCode) => {
const sgClient = require('./client');
sgClient.setApiKey('SendGrid API Key');
sgClient.setApiKey('SG.API Key');
sgClient.setDefaultRequest('baseUrl', baseUrl);
sgClient.setDefaultHeader('X-Mock', statusCode);
return sgClient
Expand All @@ -15,18 +17,17 @@ const testRequest = (request, statusCode) => {

describe('client', () => {
const sgClient = require('./client');
let consoleWarnSpy;

describe('setApiKey', () => {
let consoleWarnSpy;

beforeEach(() => {
consoleWarnSpy = sinon.spy(console, 'warn');
});
beforeEach(() => {
consoleWarnSpy = sinon.spy(console, 'warn');
});

afterEach(() => {
console.warn.restore();
});
afterEach(() => {
console.warn.restore();
});

describe('setApiKey', () => {
it('should not log a warning for a proper API key value', () => {
sgClient.setApiKey('SG.1234567890');
expect(consoleWarnSpy.notCalled).to.equal(true);
Expand All @@ -36,6 +37,42 @@ describe('client', () => {
sgClient.setApiKey(undefined);
expect(consoleWarnSpy.calledOnce).to.equal(true);
});

it('should send requests to the SendGrid path', () => {
const scope = nock('https://api.sendgrid.com')
.matchHeader('Authorization', /^Bearer SG\.1234567890$/)
.get('/')
.reply(200, 'test response');

sgClient.setApiKey('SG.1234567890');

return sgClient.request({})
.then(() => scope.done());
});
});

describe('setTwilioEmailAuth', () => {
it('should not log a warning for proper creds', () => {
sgClient.setTwilioEmailAuth('username', 'password');
expect(consoleWarnSpy.notCalled).to.equal(true);
});

it('should log a warning for a null password', () => {
sgClient.setTwilioEmailAuth('username', null);
expect(consoleWarnSpy.calledOnce).to.equal(true);
});

it('should send requests to the Twilio Email path', () => {
const scope = nock('https://email.twilio.com')
.matchHeader('Authorization', /^Basic dXNlcm5hbWU6cGFzc3dvcmQ=$/)
.get('/')
.reply(200, 'test response');

sgClient.setTwilioEmailAuth('username', 'password');

return sgClient.request({})
.then(() => scope.done());
});
});
});

Expand Down
Loading

0 comments on commit 39ea910

Please sign in to comment.