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 data residency for eu and global regions #1390

Merged
merged 10 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
timeout-minutes: 20
strategy:
matrix:
node: [ '6', '7', '8', '10', '12', '14', '16', 'lts' ]
node: [10, 12, 14, 16, lts]
env:
version: ${{ matrix.node }}
DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }}
Expand Down
37 changes: 37 additions & 0 deletions docs/use-cases/data-residency-set-hostname.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Choosing a hostname to send messages to
shrutiburman marked this conversation as resolved.
Show resolved Hide resolved

Use the `setDataResidency` setter to specify which host to send to:

Send to EU (hostname: `https://api.eu.sendgrid.com/`)
```js
const sgMail = require('@sendgrid/mail');
sgMail.setDataResidency('eu');
const msg = {
to: 'recipient@example.org',
from: 'sender@example.org',
subject: 'Hello world',
text: 'Hello plain world!',
html: '<p>Hello HTML world!</p>',
};
sgMail.send(msg);
```
Send to Global region, this is also the default host, if the setter is not used
(hostname: `https://api.sendgrid.com/`)
```js
const sgMail = require('@sendgrid/mail');
sgMail.setDataResidency('global');
const msg = {
to: 'recipient@example.org',
from: 'sender@example.org',
subject: 'Hello world',
text: 'Hello plain world!',
html: '<p>Hello HTML world!</p>',
};
sgMail.send(msg);
```

## Limitations

1. Emails can only be sent to two hosts for now; 'eu' (https://api.eu.sendgrid.com/) and 'global' (https://api.eu.sendgrid.com/)
2. The default hostname is https://api.sendgrid.com/
3. The valid values for `region` in `client.setDataResidency(region)` are only `eu` and `global`. Case-sensitive.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"moment": "^2.19.3",
"sinon": "^2.3.2",
"sinon-chai": "^2.10.0",
"typescript": "^3.7.4"
"typescript": "^4.0.0"
},
"scripts": {
"lint": "if [ `node --version | cut -d'.' -f1 | cut -c 2` -ge \"8\" ]; then eslint . --fix; else echo \"eslint is not available for node < 8.0\"; fi",
Expand Down
19 changes: 19 additions & 0 deletions packages/client/src/classes/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const API_KEY_PREFIX = 'SG.';
const SENDGRID_BASE_URL = 'https://api.sendgrid.com/';
const TWILIO_BASE_URL = 'https://email.twilio.com/';

// Initialize the allowed regions and their corresponding hosts
const REGION_HOST_MAP = {
eu: 'https://api.eu.sendgrid.com/',
global: 'https://api.sendgrid.com/',
};
class Client {
constructor() {
this.auth = '';
Expand Down Expand Up @@ -94,6 +99,20 @@ class Client {
return this;
}

/**
* Global is the default residency (or region)
* Global region means the message will be sent through https://api.sendgrid.com
* EU region means the message will be sent through https://api.eu.sendgrid.com
**/
setDataResidency(region) {
if (!REGION_HOST_MAP.hasOwnProperty(region)) {
console.warn('Region can only be "global" or "eu".');
} else {
this.setDefaultRequest('baseUrl', REGION_HOST_MAP[region]);
}
return this;
}

createHeaders(data) {
// Merge data with default headers.
const headers = mergeData(this.defaultHeaders, data);
Expand Down
5 changes: 5 additions & 0 deletions packages/client/src/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ declare class Client {
*/
setDefaultRequest<K extends keyof ClientRequest>(key: K | ClientRequest, value ?: ClientRequest[K]): this;

/**
* Sets the data residency as per region provided
*/
setDataResidency(region: string): this;

/**
* Create headers for request
*/
Expand Down
46 changes: 45 additions & 1 deletion packages/client/src/client.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';
const nock = require('nock');

const sgClient = require('./client');
const testClient = require('./client');
const testRequest = (request, statusCode) => {
const sgClient = require('./client');
sgClient.setApiKey('SG.API Key');
Expand Down Expand Up @@ -3091,3 +3092,46 @@ describe('test_whitelabel_links__link_id__subuser_post', () => {
return testRequest(request, 200);
});
});

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

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

it('should send to host EU', () => {
testClient.setDataResidency('eu');
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.eu.sendgrid.com/');
});
it('should send to host Global/default', () => {
testClient.setDataResidency('global');
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/');
});
it('should override the existing set hostname, if data residency setter is called after', () => {
testClient.setApiKey('SG.1234567890');
testClient.setDataResidency('eu');
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.eu.sendgrid.com/');
});
it('should give a warning if the provided value is not allowed', () => {
testClient.setDataResidency('');
expect(consoleWarnSpy.calledOnce).to.equal(true);
});
it('should give a warning if the provided value is null', () => {
testClient.setDataResidency(null);
expect(consoleWarnSpy.calledOnce).to.equal(true);
});
it('should give precedence to the order of execution', () => {
testClient.setDataResidency('eu');
testClient.setApiKey('SG.1234567890');
gladysmae08 marked this conversation as resolved.
Show resolved Hide resolved
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/');
});
it('should have default value of hostname as https://api.sendgrid.com/', () => {
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/');
});
});

Loading