From 91b4cb6fb08cb37beac843cf4533a2a9f22b8a4c Mon Sep 17 00:00:00 2001 From: sburman Date: Fri, 17 Nov 2023 16:46:48 +0530 Subject: [PATCH 1/8] init --- packages/client/src/classes/client.js | 13 +++++++++++++ packages/client/src/client.spec.js | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/packages/client/src/classes/client.js b/packages/client/src/classes/client.js index c91768d4d..8d756df3a 100644 --- a/packages/client/src/classes/client.js +++ b/packages/client/src/classes/client.js @@ -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: 'api.eu.sendgrid.com', + global: 'api.sendgrid.com', +}; class Client { constructor() { this.auth = ''; @@ -94,6 +99,14 @@ class Client { return this; } + 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]); + } + } + createHeaders(data) { // Merge data with default headers. const headers = mergeData(this.defaultHeaders, data); diff --git a/packages/client/src/client.spec.js b/packages/client/src/client.spec.js index 65e0e5997..536515615 100644 --- a/packages/client/src/client.spec.js +++ b/packages/client/src/client.spec.js @@ -1,5 +1,6 @@ 'use strict'; const nock = require('nock'); +const sgClient = require('./client'); const testRequest = (request, statusCode) => { const sgClient = require('./client'); @@ -3091,3 +3092,12 @@ describe('test_whitelabel_links__link_id__subuser_post', () => { return testRequest(request, 200); }); }); + +describe('setDataResidency', () => { + const sgClient = require('./client'); + sgClient.setDataResidency('eu'); + + it('should have host as eu', () => { + expect(sgClient.baseUrl).to.equal('api.eu.sendgrid.com'); + }); +}); From 50a8691fa9ccc240e4d2a7c0e8f953e7f2e39f73 Mon Sep 17 00:00:00 2001 From: sburman Date: Fri, 17 Nov 2023 17:17:09 +0530 Subject: [PATCH 2/8] Added tests --- .github/workflows/test-and-deploy.yml | 2 +- package.json | 2 +- packages/client/src/classes/client.js | 5 +-- packages/client/src/client.d.ts | 5 +++ packages/client/src/client.spec.js | 44 ++++++++++++++++++++++++--- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index b0b5ee81b..aeebb9267 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - node: [ '6', '7', '8', '10', '12', '14', '16', 'lts' ] + node: [10, 14, 16, lts] env: version: ${{ matrix.node }} DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} diff --git a/package.json b/package.json index 628a4aced..bb189642e 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/client/src/classes/client.js b/packages/client/src/classes/client.js index 8d756df3a..c1ec0ed41 100644 --- a/packages/client/src/classes/client.js +++ b/packages/client/src/classes/client.js @@ -17,8 +17,8 @@ const TWILIO_BASE_URL = 'https://email.twilio.com/'; // Initialize the allowed regions and their corresponding hosts const REGION_HOST_MAP = { - eu: 'api.eu.sendgrid.com', - global: 'api.sendgrid.com', + eu: 'https://api.eu.sendgrid.com/', + global: 'https://api.sendgrid.com/', }; class Client { constructor() { @@ -105,6 +105,7 @@ class Client { } else { this.setDefaultRequest('baseUrl', REGION_HOST_MAP[region]); } + return this; } createHeaders(data) { diff --git a/packages/client/src/client.d.ts b/packages/client/src/client.d.ts index fb84f7cb1..84075d1f9 100644 --- a/packages/client/src/client.d.ts +++ b/packages/client/src/client.d.ts @@ -30,6 +30,11 @@ declare class Client { */ setDefaultRequest(key: K | ClientRequest, value ?: ClientRequest[K]): this; + /** + * Sets the data residency as per region provided + */ + setDataResidency(region: string): this; + /** * Create headers for request */ diff --git a/packages/client/src/client.spec.js b/packages/client/src/client.spec.js index 536515615..2dfc8a4a9 100644 --- a/packages/client/src/client.spec.js +++ b/packages/client/src/client.spec.js @@ -1,7 +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'); @@ -3094,10 +3094,44 @@ describe('test_whitelabel_links__link_id__subuser_post', () => { }); describe('setDataResidency', () => { - const sgClient = require('./client'); - sgClient.setDataResidency('eu'); + const testClient = require('./client'); + let consoleWarnSpy; - it('should have host as eu', () => { - expect(sgClient.baseUrl).to.equal('api.eu.sendgrid.com'); + 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'); + 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/'); }); }); + From 2f7c9eae5dfee949c584652c526dab346c152941 Mon Sep 17 00:00:00 2001 From: sburman Date: Mon, 20 Nov 2023 13:57:01 +0530 Subject: [PATCH 3/8] Add an example readme --- docs/use-cases/data-residency-set-hostname.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/use-cases/data-residency-set-hostname.md diff --git a/docs/use-cases/data-residency-set-hostname.md b/docs/use-cases/data-residency-set-hostname.md new file mode 100644 index 000000000..5b977d4eb --- /dev/null +++ b/docs/use-cases/data-residency-set-hostname.md @@ -0,0 +1,37 @@ +# Choosing a hostname to send messages to + +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: '

Hello HTML world!

', +}; +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: '

Hello HTML world!

', +}; +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. From a2467592748515a45dc26a7c53c5b3af5c1af036 Mon Sep 17 00:00:00 2001 From: sburman Date: Mon, 20 Nov 2023 14:05:19 +0530 Subject: [PATCH 4/8] Add v12 --- .github/workflows/test-and-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index aeebb9267..b52169ee9 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - node: [10, 14, 16, lts] + node: [10, 12, 14, 16, lts] env: version: ${{ matrix.node }} DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} From 1c79ce7230ac7f6e988adb7bef9f6ba4739dc127 Mon Sep 17 00:00:00 2001 From: sburman Date: Tue, 21 Nov 2023 11:10:57 +0530 Subject: [PATCH 5/8] nit --- packages/client/src/classes/client.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/client/src/classes/client.js b/packages/client/src/classes/client.js index c1ec0ed41..713dcc310 100644 --- a/packages/client/src/classes/client.js +++ b/packages/client/src/classes/client.js @@ -99,6 +99,11 @@ 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".'); From a21b1698455a1e7c4094ec26ca78f0d83905c9f3 Mon Sep 17 00:00:00 2001 From: sburman Date: Wed, 22 Nov 2023 00:36:07 +0530 Subject: [PATCH 6/8] update limitations --- docs/use-cases/data-residency-set-hostname.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/use-cases/data-residency-set-hostname.md b/docs/use-cases/data-residency-set-hostname.md index 5b977d4eb..f7ace11b4 100644 --- a/docs/use-cases/data-residency-set-hostname.md +++ b/docs/use-cases/data-residency-set-hostname.md @@ -32,6 +32,7 @@ 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/) +1. Setting the API Key (via `client.setApiKey()`) or Twilio Authentication (via `client.setTwilioEmailAuth()`) will override the hostname to default value. Use the setter call after this set-up. +2. 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. From b356716b65bac49069432cc7af4fcd94b6a8debc Mon Sep 17 00:00:00 2001 From: sburman Date: Thu, 23 Nov 2023 18:49:05 +0530 Subject: [PATCH 7/8] updates to baseUrl setter --- packages/client/src/classes/client.js | 8 ++++++-- packages/client/src/client.spec.js | 23 ++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/client/src/classes/client.js b/packages/client/src/classes/client.js index 713dcc310..c0c051fe6 100644 --- a/packages/client/src/classes/client.js +++ b/packages/client/src/classes/client.js @@ -24,6 +24,7 @@ class Client { constructor() { this.auth = ''; this.impersonateSubuser = ''; + this.sendgrid_region = ''; this.defaultHeaders = { Accept: 'application/json', @@ -43,8 +44,10 @@ class Client { setApiKey(apiKey) { this.auth = 'Bearer ' + apiKey; - this.setDefaultRequest('baseUrl', SENDGRID_BASE_URL); - + // this means that region was never set before + if (this.sendgrid_region == '') { + this.setDefaultRequest('baseUrl', SENDGRID_BASE_URL); + } if (!this.isValidApiKey(apiKey)) { console.warn(`API key does not start with "${API_KEY_PREFIX}".`); } @@ -108,6 +111,7 @@ class Client { 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; diff --git a/packages/client/src/client.spec.js b/packages/client/src/client.spec.js index 2dfc8a4a9..b2be92866 100644 --- a/packages/client/src/client.spec.js +++ b/packages/client/src/client.spec.js @@ -1,7 +1,7 @@ 'use strict'; const nock = require('nock'); const sgClient = require('./client'); -const testClient = require('./client'); +let testClient = require('./client'); const testRequest = (request, statusCode) => { const sgClient = require('./client'); sgClient.setApiKey('SG.API Key'); @@ -3094,16 +3094,18 @@ describe('test_whitelabel_links__link_id__subuser_post', () => { }); describe('setDataResidency', () => { - const testClient = require('./client'); 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(''); + }); it('should send to host EU', () => { testClient.setDataResidency('eu'); expect(testClient.defaultRequest.baseUrl).to.equal('https://api.eu.sendgrid.com/'); @@ -3111,6 +3113,7 @@ describe('setDataResidency', () => { 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'); @@ -3125,13 +3128,19 @@ describe('setDataResidency', () => { testClient.setDataResidency(null); expect(consoleWarnSpy.calledOnce).to.equal(true); }); - it('should give precedence to the order of execution', () => { + it('setting the API Key wont reset the region set', () => { testClient.setDataResidency('eu'); testClient.setApiKey('SG.1234567890'); - expect(testClient.defaultRequest.baseUrl).to.equal('https://api.sendgrid.com/'); + expect(testClient.defaultRequest.baseUrl).to.equal('https://api.eu.sendgrid.com/'); + expect(testClient.sendgrid_region).to.equal('eu'); }); - it('should have default value of hostname as https://api.sendgrid.com/', () => { + 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'); + + }); }); From 8bfe5cd9a5da047b4b3a5068495cbe40c1ab3583 Mon Sep 17 00:00:00 2001 From: sburman Date: Thu, 30 Nov 2023 10:52:18 +0530 Subject: [PATCH 8/8] review comments --- docs/use-cases/data-residency-set-hostname.md | 11 +++++------ packages/client/src/classes/client.js | 10 ++++------ packages/client/src/client.spec.js | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/use-cases/data-residency-set-hostname.md b/docs/use-cases/data-residency-set-hostname.md index f7ace11b4..0fc0927d5 100644 --- a/docs/use-cases/data-residency-set-hostname.md +++ b/docs/use-cases/data-residency-set-hostname.md @@ -1,8 +1,8 @@ -# Choosing a hostname to send messages to +# Choosing a data-residency to send messages to Use the `setDataResidency` setter to specify which host to send to: -Send to EU (hostname: `https://api.eu.sendgrid.com/`) +Send to EU (data-residency: `https://api.eu.sendgrid.com/`) ```js const sgMail = require('@sendgrid/mail'); sgMail.setDataResidency('eu'); @@ -16,7 +16,7 @@ const msg = { sgMail.send(msg); ``` Send to Global region, this is also the default host, if the setter is not used -(hostname: `https://api.sendgrid.com/`) +(data-residency: `https://api.sendgrid.com/`) ```js const sgMail = require('@sendgrid/mail'); sgMail.setDataResidency('global'); @@ -32,7 +32,6 @@ sgMail.send(msg); ## Limitations -1. Setting the API Key (via `client.setApiKey()`) or Twilio Authentication (via `client.setTwilioEmailAuth()`) will override the hostname to default value. Use the setter call after this set-up. -2. 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/ +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. diff --git a/packages/client/src/classes/client.js b/packages/client/src/classes/client.js index c0c051fe6..22357c0ba 100644 --- a/packages/client/src/classes/client.js +++ b/packages/client/src/classes/client.js @@ -14,7 +14,7 @@ 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/', @@ -24,7 +24,7 @@ class Client { constructor() { this.auth = ''; this.impersonateSubuser = ''; - this.sendgrid_region = ''; + this.sendgrid_region = SENDGRID_REGION; this.defaultHeaders = { Accept: 'application/json', @@ -44,10 +44,8 @@ class Client { setApiKey(apiKey) { this.auth = 'Bearer ' + apiKey; - // this means that region was never set before - if (this.sendgrid_region == '') { - 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}".`); } diff --git a/packages/client/src/client.spec.js b/packages/client/src/client.spec.js index b2be92866..277adc287 100644 --- a/packages/client/src/client.spec.js +++ b/packages/client/src/client.spec.js @@ -3104,7 +3104,7 @@ describe('setDataResidency', () => { }); 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(''); + expect(testClient.sendgrid_region).to.equal('global'); }); it('should send to host EU', () => { testClient.setDataResidency('eu');