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 all 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
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 data-residency to send messages to

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

Send to EU (data-residency: `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
(data-residency: `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 data-residency is https://api.sendgrid.com/
3. The valid values for `region` in `client.setDataResidency(region)` are only `eu` and `global`. Case-sensitive.
25 changes: 23 additions & 2 deletions packages/client/src/classes/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ const {
const API_KEY_PREFIX = 'SG.';
const SENDGRID_BASE_URL = 'https://api.sendgrid.com/';
const TWILIO_BASE_URL = 'https://email.twilio.com/';

const SENDGRID_REGION = 'global';
// 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 = '';
this.impersonateSubuser = '';
this.sendgrid_region = SENDGRID_REGION;

this.defaultHeaders = {
Accept: 'application/json',
Expand All @@ -38,7 +44,7 @@ class Client {

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

if (!this.isValidApiKey(apiKey)) {
console.warn(`API key does not start with "${API_KEY_PREFIX}".`);
Expand Down Expand Up @@ -94,6 +100,21 @@ 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.sendgrid_region = region;
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
55 changes: 54 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');
let testClient = require('./client');
const testRequest = (request, statusCode) => {
const sgClient = require('./client');
sgClient.setApiKey('SG.API Key');
Expand Down Expand Up @@ -3091,3 +3092,55 @@ describe('test_whitelabel_links__link_id__subuser_post', () => {
return testRequest(request, 200);
});
});

describe('setDataResidency', () => {
let consoleWarnSpy;
beforeEach(() => {
testClient = require('./client');
consoleWarnSpy = sinon.spy(console, 'warn');
});
afterEach(() => {
console.warn.restore();
});
it('should have default value of hostname as https://api.sendgrid.com/', () => {
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/');
expect(testClient.sendgrid_region).to.equal('global');
});
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/');
expect(testClient.sendgrid_region).to.equal('global');
});
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('setting the API Key wont reset the region set', () => {
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.eu.sendgrid.com/');
expect(testClient.sendgrid_region).to.equal('eu');
});
it('should send to host global and then call setApiKey', () => {
testClient.setDataResidency('global');
testClient.setApiKey('SG.1234567890');
expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/');
expect(testClient.sendgrid_region).to.equal('global');


});
});

Loading