Skip to content

Commit

Permalink
feat: added data residency for eu and global regions (sendgrid#469)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
tiwarishubham635 authored and qhenkart committed Mar 15, 2024
1 parent 748756d commit 489a749
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
50 changes: 50 additions & 0 deletions base_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"errors"
"net/http"
"net/url"
"strconv"
"time"

Expand All @@ -20,6 +21,11 @@ const (
rateLimitSleep = 1100
)

var allowedRegionsHostMap = map[string]string{
"eu": "https://api.eu.sendgrid.com",
"global": "https://api.sendgrid.com",
}

type options struct {
Auth string
Endpoint string
Expand Down Expand Up @@ -55,6 +61,50 @@ func requestNew(options options) rest.Request {
}
}

// extractEndpoint extracts the endpoint from a baseURL
func extractEndpoint(link string) (string, error) {
parsedURL, err := url.Parse(link)
if err != nil {
return "", err
}

return parsedURL.Path, nil
}

// SetHost changes the baseURL of the request with the host passed
/*
* This allows support for global and eu regions only. This set will likely expand in the future.
* Global should be the default
* Global region means the message should be sent through:
* HTTP: api.sendgrid.com
* EU region means the message should be sent through:
* HTTP: api.eu.sendgrid.com
*/
// @return [Request] the modified request object
func SetHost(request rest.Request, host string) (rest.Request, error) {
endpoint, err := extractEndpoint(request.BaseURL)
if err != nil {
return request, err
}

request.BaseURL = host + endpoint
return request, nil
}

// SetDataResidency modifies the host as per the region
// @return [Request] the modified request object
func SetDataResidency(request rest.Request, region string) (rest.Request, error) {
regionalHost, present := allowedRegionsHostMap[region]
if !present {
return request, errors.New("error: region can only be \"eu\" or \"global\"")
}
request, err := SetHost(request, regionalHost)
if err != nil {
return request, err
}
return request, nil
}

// Send sends an email through Twilio SendGrid
func (cl *Client) Send(email *mail.SGMailV3) (*rest.Response, error) {
return cl.SendWithContext(context.Background(), email)
Expand Down
100 changes: 100 additions & 0 deletions examples/dataresidency/setRegion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"fmt"
"log"
"os"

"github.com/sendgrid/rest"
"github.com/sendgrid/sendgrid-go/helpers/mail"

"github.com/sendgrid/sendgrid-go"
)

var SAMPLE_EMAIL = "[email protected]"

// SetDataResidency : Set region for sendgrid.
func SetDataResidencyGlobal() {
message := buildHelloEmail()
request, err := buildSendgridObj("global")
if err != nil {
log.Println(err)
} else {
request.Body = mail.GetRequestBody(message)
response, err := sendgrid.API(request)
if err != nil {
log.Println(err)
} else {
fmt.Println(response.StatusCode)
fmt.Println(response.Body)
fmt.Println(response.Headers)
}
}
}

func SetDataResidencyEu() {
message := buildHelloEmail()
request, err := buildSendgridObj("eu")
if err != nil {
log.Println(err)
} else {
request.Body = mail.GetRequestBody(message)
response, err := sendgrid.API(request)
if err != nil {
log.Println(err)
} else {
fmt.Println(response.StatusCode)
fmt.Println(response.Body)
fmt.Println(response.Headers)
}
}
}

func SetDataResidencyDefault() {
message := buildHelloEmail()
request := sendgrid.GetRequest(os.Getenv("SENDGRID_API_KEY"), "/v3/mail/send", "")
request.Method = "POST"
request.Body = mail.GetRequestBody(message)
response, err := sendgrid.API(request)
if err != nil {
log.Println(err)
} else {
fmt.Println(response.StatusCode)
fmt.Println(response.Body)
fmt.Println(response.Headers)
}
}

func buildHelloEmail() *mail.SGMailV3 {
// Note that when you use this constructor an initial personalization object
// is created for you. It can be accessed via
// mail.personalization.get(0) as it is a List object

from := mail.NewEmail("test_user", SAMPLE_EMAIL)
subject := "Sending with Twilio SendGrid is Fun"
to := mail.NewEmail("test_user", SAMPLE_EMAIL)
plainTextContent := "and easy to do anywhere, even with Go"
htmlContent := "<strong>and easy to do anywhere, even with Go</strong>"
message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
email := mail.NewEmail("test_user", SAMPLE_EMAIL)

p := mail.NewPersonalization()
p.AddTos(email)
message.AddPersonalizations(p)

return message
}

func buildSendgridObj(region string) (rest.Request, error) {
request := sendgrid.GetRequest(os.Getenv("SENDGRID_API_KEY"), "/v3/mail/send", "")
request.Method = "POST"
request, err := sendgrid.SetDataResidency(request, region)
if err != nil {
return request, err
}
return request, nil
}

func main() {
// add your function calls here
}
47 changes: 47 additions & 0 deletions sendgrid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,53 @@ func TestGetRequestSubuser(t *testing.T) {
ShouldHaveHeaders(&request, t)
}

func TestSetDataResidencyEU(t *testing.T) {
request := GetRequest("API_KEY", "", "")
request, err := SetDataResidency(request, "eu")
assert.Nil(t, err)
assert.Equal(t, "https://api.eu.sendgrid.com", request.BaseURL, "Host not correct as per the region")
}

func TestSetDataResidencyGlobal(t *testing.T) {
request := GetRequest("API_KEY", "", "https://api.sendgrid.com")
request, err := SetDataResidency(request, "global")
assert.Nil(t, err)
assert.Equal(t, "https://api.sendgrid.com", request.BaseURL, "Host not correct as per the region")
}

func TestSetDataResidencyOverrideHost(t *testing.T) {
request := GetRequest("API_KEY", "", "https://test.api.com")
request, err := SetDataResidency(request, "eu")
assert.Nil(t, err)
assert.Equal(t, "https://api.eu.sendgrid.com", request.BaseURL, "Host not correct as per the region")
}

func TestSetDataResidencyOverrideDataResidency(t *testing.T) {
request := GetRequest("API_KEY", "", "")
request, err := SetDataResidency(request, "eu")
assert.Nil(t, err)
request, err = SetHost(request, "https://test.api.com")
assert.Nil(t, err)
assert.Equal(t, "https://test.api.com", request.BaseURL, "Host not correct as per the region")
}

func TestSetDataResidencyIncorrectRegion(t *testing.T) {
request := GetRequest("API_KEY", "", "")
_, err := SetDataResidency(request, "foo")
assert.NotNil(t, err, "error: region can only be \"eu\" or \"global\"")
}

func TestSetDataResidencyNullRegion(t *testing.T) {
request := GetRequest("API_KEY", "", "")
_, err := SetDataResidency(request, "")
assert.NotNil(t, err, "error: region can only be \"eu\" or \"global\"")
}

func TestSetDataResidencyDefaultRegion(t *testing.T) {
request := GetRequest("API_KEY", "", "")
assert.Equal(t, "https://api.sendgrid.com", request.BaseURL, "Host not correct as per the region")
}

func getRequest(endpoint string) rest.Request {
return GetRequest("SENDGRID_APIKEY", endpoint, "")
}
Expand Down

0 comments on commit 489a749

Please sign in to comment.