Skip to content

Commit e7f240a

Browse files
feat: added data residency for eu and global regions (#469)
* feat: added data residency for eu and global regions * feat: added data residency for eu and global regions * fix: added setters for host and data residency * chore: corrected the naming of test cases * chore: added inline documentation for SetDataResidency and SetHost * chore: added comment for SetDataResidency
1 parent 72d6eb8 commit e7f240a

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed

base_interface.go

+50
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"errors"
88
"net/http"
9+
"net/url"
910
"strconv"
1011
"time"
1112

@@ -20,6 +21,11 @@ const (
2021
rateLimitSleep = 1100
2122
)
2223

24+
var allowedRegionsHostMap = map[string]string{
25+
"eu": "https://api.eu.sendgrid.com",
26+
"global": "https://api.sendgrid.com",
27+
}
28+
2329
type options struct {
2430
Auth string
2531
Endpoint string
@@ -55,6 +61,50 @@ func requestNew(options options) rest.Request {
5561
}
5662
}
5763

64+
// extractEndpoint extracts the endpoint from a baseURL
65+
func extractEndpoint(link string) (string, error) {
66+
parsedURL, err := url.Parse(link)
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
return parsedURL.Path, nil
72+
}
73+
74+
// SetHost changes the baseURL of the request with the host passed
75+
/*
76+
* This allows support for global and eu regions only. This set will likely expand in the future.
77+
* Global should be the default
78+
* Global region means the message should be sent through:
79+
* HTTP: api.sendgrid.com
80+
* EU region means the message should be sent through:
81+
* HTTP: api.eu.sendgrid.com
82+
*/
83+
// @return [Request] the modified request object
84+
func SetHost(request rest.Request, host string) (rest.Request, error) {
85+
endpoint, err := extractEndpoint(request.BaseURL)
86+
if err != nil {
87+
return request, err
88+
}
89+
90+
request.BaseURL = host + endpoint
91+
return request, nil
92+
}
93+
94+
// SetDataResidency modifies the host as per the region
95+
// @return [Request] the modified request object
96+
func SetDataResidency(request rest.Request, region string) (rest.Request, error) {
97+
regionalHost, present := allowedRegionsHostMap[region]
98+
if !present {
99+
return request, errors.New("error: region can only be \"eu\" or \"global\"")
100+
}
101+
request, err := SetHost(request, regionalHost)
102+
if err != nil {
103+
return request, err
104+
}
105+
return request, nil
106+
}
107+
58108
// Send sends an email through Twilio SendGrid
59109
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
60110
return cl.SendWithContext(context.Background(), email)

examples/dataresidency/setRegion.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
8+
"github.com/sendgrid/rest"
9+
"github.com/sendgrid/sendgrid-go/helpers/mail"
10+
11+
"github.com/sendgrid/sendgrid-go"
12+
)
13+
14+
var SAMPLE_EMAIL = "[email protected]"
15+
16+
// SetDataResidency : Set region for sendgrid.
17+
func SetDataResidencyGlobal() {
18+
message := buildHelloEmail()
19+
request, err := buildSendgridObj("global")
20+
if err != nil {
21+
log.Println(err)
22+
} else {
23+
request.Body = mail.GetRequestBody(message)
24+
response, err := sendgrid.API(request)
25+
if err != nil {
26+
log.Println(err)
27+
} else {
28+
fmt.Println(response.StatusCode)
29+
fmt.Println(response.Body)
30+
fmt.Println(response.Headers)
31+
}
32+
}
33+
}
34+
35+
func SetDataResidencyEu() {
36+
message := buildHelloEmail()
37+
request, err := buildSendgridObj("eu")
38+
if err != nil {
39+
log.Println(err)
40+
} else {
41+
request.Body = mail.GetRequestBody(message)
42+
response, err := sendgrid.API(request)
43+
if err != nil {
44+
log.Println(err)
45+
} else {
46+
fmt.Println(response.StatusCode)
47+
fmt.Println(response.Body)
48+
fmt.Println(response.Headers)
49+
}
50+
}
51+
}
52+
53+
func SetDataResidencyDefault() {
54+
message := buildHelloEmail()
55+
request := sendgrid.GetRequest(os.Getenv("SENDGRID_API_KEY"), "/v3/mail/send", "")
56+
request.Method = "POST"
57+
request.Body = mail.GetRequestBody(message)
58+
response, err := sendgrid.API(request)
59+
if err != nil {
60+
log.Println(err)
61+
} else {
62+
fmt.Println(response.StatusCode)
63+
fmt.Println(response.Body)
64+
fmt.Println(response.Headers)
65+
}
66+
}
67+
68+
func buildHelloEmail() *mail.SGMailV3 {
69+
// Note that when you use this constructor an initial personalization object
70+
// is created for you. It can be accessed via
71+
// mail.personalization.get(0) as it is a List object
72+
73+
from := mail.NewEmail("test_user", SAMPLE_EMAIL)
74+
subject := "Sending with Twilio SendGrid is Fun"
75+
to := mail.NewEmail("test_user", SAMPLE_EMAIL)
76+
plainTextContent := "and easy to do anywhere, even with Go"
77+
htmlContent := "<strong>and easy to do anywhere, even with Go</strong>"
78+
message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
79+
email := mail.NewEmail("test_user", SAMPLE_EMAIL)
80+
81+
p := mail.NewPersonalization()
82+
p.AddTos(email)
83+
message.AddPersonalizations(p)
84+
85+
return message
86+
}
87+
88+
func buildSendgridObj(region string) (rest.Request, error) {
89+
request := sendgrid.GetRequest(os.Getenv("SENDGRID_API_KEY"), "/v3/mail/send", "")
90+
request.Method = "POST"
91+
request, err := sendgrid.SetDataResidency(request, region)
92+
if err != nil {
93+
return request, err
94+
}
95+
return request, nil
96+
}
97+
98+
func main() {
99+
// add your function calls here
100+
}

sendgrid_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,53 @@ func TestGetRequestSubuser(t *testing.T) {
8181
ShouldHaveHeaders(&request, t)
8282
}
8383

84+
func TestSetDataResidencyEU(t *testing.T) {
85+
request := GetRequest("API_KEY", "", "")
86+
request, err := SetDataResidency(request, "eu")
87+
assert.Nil(t, err)
88+
assert.Equal(t, "https://api.eu.sendgrid.com", request.BaseURL, "Host not correct as per the region")
89+
}
90+
91+
func TestSetDataResidencyGlobal(t *testing.T) {
92+
request := GetRequest("API_KEY", "", "https://api.sendgrid.com")
93+
request, err := SetDataResidency(request, "global")
94+
assert.Nil(t, err)
95+
assert.Equal(t, "https://api.sendgrid.com", request.BaseURL, "Host not correct as per the region")
96+
}
97+
98+
func TestSetDataResidencyOverrideHost(t *testing.T) {
99+
request := GetRequest("API_KEY", "", "https://test.api.com")
100+
request, err := SetDataResidency(request, "eu")
101+
assert.Nil(t, err)
102+
assert.Equal(t, "https://api.eu.sendgrid.com", request.BaseURL, "Host not correct as per the region")
103+
}
104+
105+
func TestSetDataResidencyOverrideDataResidency(t *testing.T) {
106+
request := GetRequest("API_KEY", "", "")
107+
request, err := SetDataResidency(request, "eu")
108+
assert.Nil(t, err)
109+
request, err = SetHost(request, "https://test.api.com")
110+
assert.Nil(t, err)
111+
assert.Equal(t, "https://test.api.com", request.BaseURL, "Host not correct as per the region")
112+
}
113+
114+
func TestSetDataResidencyIncorrectRegion(t *testing.T) {
115+
request := GetRequest("API_KEY", "", "")
116+
_, err := SetDataResidency(request, "foo")
117+
assert.NotNil(t, err, "error: region can only be \"eu\" or \"global\"")
118+
}
119+
120+
func TestSetDataResidencyNullRegion(t *testing.T) {
121+
request := GetRequest("API_KEY", "", "")
122+
_, err := SetDataResidency(request, "")
123+
assert.NotNil(t, err, "error: region can only be \"eu\" or \"global\"")
124+
}
125+
126+
func TestSetDataResidencyDefaultRegion(t *testing.T) {
127+
request := GetRequest("API_KEY", "", "")
128+
assert.Equal(t, "https://api.sendgrid.com", request.BaseURL, "Host not correct as per the region")
129+
}
130+
84131
func getRequest(endpoint string) rest.Request {
85132
return GetRequest("SENDGRID_APIKEY", endpoint, "")
86133
}

0 commit comments

Comments
 (0)