Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Twilio Email #1093

Merged
merged 2 commits into from Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 26 additions & 22 deletions docs/use-cases/README.md
@@ -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
@@ -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
@@ -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
@@ -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
Expand Up @@ -30,6 +30,9 @@
"@sendgrid/helpers": "^7.0.0",
"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
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
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
@@ -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');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

});

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