Skip to content

Commit 591addb

Browse files
authored
feat: add support for region and edge values in url (#62)
1 parent f42b601 commit 591addb

File tree

10 files changed

+634
-13
lines changed

10 files changed

+634
-13
lines changed

.github/workflows/golangci-lint.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ jobs:
1414
with:
1515
version: latest
1616
github-token: ${{ secrets.github_token }}
17-
args: "--enable-all --exclude-use-default=false -D errcheck -D gomnd"
1817
only-new-issues: true

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ install:
44
go build -v ./...
55

66
test:
7-
go test ./...
7+
go test -v ./...
88

99
goimports:
1010
go get golang.org/x/tools/cmd/goimports

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,29 @@ func main(){
6767
}
6868
```
6969

70+
### Specify a Region and/or Edge
71+
72+
```go
73+
package main
74+
75+
import (
76+
"github.com/twilio/twilio-go/twilio"
77+
"os"
78+
)
79+
80+
func main() {
81+
accountSid := os.Getenv("TWILIO_ACCOUNT_SID")
82+
authToken := os.Getenv("TWILIO_AUTH_TOKEN")
83+
client := twilio.NewClient(accountSid, authToken)
84+
client.SetRegion("au1")
85+
client.SetEdge("sydney")
86+
}
87+
88+
```
89+
This will result in the `hostname` transforming from `api.twilio.com` to `api.sydney.au1.twilio.com`.
90+
91+
A Twilio client constructed without these parameters will also look for `TWILIO_REGION` and `TWILIO_EDGE` variables inside the current environment.
92+
7093
### Buy a phone number
7194

7295
```go
@@ -136,7 +159,7 @@ func main() {
136159
```
137160

138161
### Make a call
139-
``` go
162+
```go
140163
package main
141164

142165
import (

client/client.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ type Client struct {
2828
*Credentials
2929
HTTPClient *http.Client
3030
BaseURL string
31+
Edge string
32+
Region string
3133
}
3234

33-
// default http Client should not follow redirects and return the most recent response
35+
// default http Client should not follow redirects and return the most recent response.
3436
func defaultHTTPClient() *http.Client {
3537
return &http.Client{
3638
CheckRedirect: func(req *http.Request, via []*http.Request) error {
@@ -44,6 +46,49 @@ func (c *Client) basicAuth() (string, string) {
4446
return c.Credentials.AccountSID, c.Credentials.AuthToken
4547
}
4648

49+
// BuildHost builds the target host string taking into account region and edge configurations.
50+
func (c *Client) BuildHost(rawHost string) string {
51+
var (
52+
edge = ""
53+
region = ""
54+
pieces = strings.Split(rawHost, ".")
55+
product = pieces[0]
56+
suffix = strings.Join(pieces[len(pieces)-2:], ".")
57+
result = []string{}
58+
)
59+
60+
if len(pieces) == 4 {
61+
// product.region.twilio.com
62+
region = pieces[1]
63+
} else if len(pieces) == 5 {
64+
// product.edge.region.twilio.com
65+
edge = pieces[1]
66+
region = pieces[2]
67+
}
68+
69+
if c.Edge != "" {
70+
edge = c.Edge
71+
}
72+
73+
if c.Region != "" {
74+
region = c.Region
75+
} else if region == "" && edge != "" {
76+
region = "us1"
77+
}
78+
79+
if c.BaseURL != "" {
80+
suffix = c.BaseURL
81+
}
82+
83+
for _, item := range []string{product, edge, region, suffix} {
84+
if item != "" {
85+
result = append(result, item)
86+
}
87+
}
88+
89+
return strings.Join(result, ".")
90+
}
91+
4792
// SetTimeout sets the Timeout for HTTP requests.
4893
func (c *Client) SetTimeout(timeout time.Duration) {
4994
c.HTTPClient.Timeout = timeout
@@ -103,6 +148,8 @@ func (c Client) SendRequest(method string, rawURL string, queryParams interface{
103148
valueReader = strings.NewReader(formData.Encode())
104149
}
105150

151+
u.Host = c.BuildHost(u.Host)
152+
106153
req, err := http.NewRequest(method, u.String(), valueReader)
107154
if err != nil {
108155
return nil, err

client/client_test.go

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"net/http"
66
"net/http/httptest"
7+
"os"
78
"testing"
89
"time"
910

@@ -20,7 +21,8 @@ func NewClient(accountSid string, authToken string) *twilio.Client {
2021
c := &twilio.Client{
2122
Credentials: creds,
2223
HTTPClient: http.DefaultClient,
23-
BaseURL: "twilio.com",
24+
Edge: os.Getenv("TWILIO_EDGE"),
25+
Region: os.Getenv("TWILIO_REGION"),
2426
}
2527

2628
return c
@@ -103,7 +105,6 @@ func TestClient_SetTimeoutTimesOut(t *testing.T) {
103105
time.Sleep(100 * time.Microsecond)
104106
encoder := json.NewEncoder(writer)
105107
err := encoder.Encode(&d)
106-
107108
if err != nil {
108109
t.Error(err)
109110
}
@@ -126,7 +127,6 @@ func TestClient_SetTimeoutSucceeds(t *testing.T) {
126127
time.Sleep(100 * time.Microsecond)
127128
encoder := json.NewEncoder(writer)
128129
err := encoder.Encode(&d)
129-
130130
if err != nil {
131131
t.Error(err)
132132
}
@@ -140,3 +140,88 @@ func TestClient_SetTimeoutSucceeds(t *testing.T) {
140140
assert.NoError(t, err)
141141
assert.Equal(t, 200, resp.StatusCode)
142142
}
143+
144+
//nolint:paralleltest
145+
func TestClient_BuildHostSetRegion(t *testing.T) {
146+
// Region set via client
147+
client := NewClient("user", "pass")
148+
client.Region = "region"
149+
assert.Equal(t, "https://api.region.twilio.com", client.BuildHost("https://api.twilio.com"))
150+
assert.Equal(t, "https://api.region.twilio.com", client.BuildHost("https://api.urlRegion.twilio.com"))
151+
152+
// Region set via env vars
153+
err := os.Setenv("TWILIO_REGION", "region")
154+
if err != nil {
155+
t.Errorf("unexpected error: %v", err)
156+
}
157+
158+
client = NewClient("user", "pass")
159+
assert.Equal(t, "https://api.region.twilio.com", client.BuildHost("https://api.twilio.com"))
160+
161+
err = os.Setenv("TWILIO_REGION", "")
162+
if err != nil {
163+
t.Errorf("unexpected error: %v", err)
164+
}
165+
166+
// Region set via url
167+
assert.Equal(t, "https://api.region.twilio.com", client.BuildHost("https://api.region.twilio.com"))
168+
}
169+
170+
//nolint:paralleltest
171+
func TestClient_BuildHostSetEdgeDefaultRegion(t *testing.T) {
172+
// Edge set via client
173+
client := NewClient("user", "pass")
174+
client.Edge = "edge"
175+
assert.Equal(t, "https://api.edge.us1.twilio.com", client.BuildHost("https://api.twilio.com"))
176+
177+
// Edge set via env vars
178+
err := os.Setenv("TWILIO_EDGE", "edge")
179+
if err != nil {
180+
t.Errorf("unexpected error: %v", err)
181+
}
182+
183+
client = NewClient("user", "pass")
184+
assert.Equal(t, "https://api.edge.us1.twilio.com", client.BuildHost("https://api.twilio.com"))
185+
186+
err = os.Setenv("TWILIO_EDGE", "")
187+
if err != nil {
188+
t.Errorf("unexpected error: %v", err)
189+
}
190+
}
191+
192+
//nolint:paralleltest
193+
func TestClient_BuildHostSetEdgeRegion(t *testing.T) {
194+
// Edge and Region set via client
195+
client := NewClient("user", "pass")
196+
client.Edge = "edge"
197+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.region.twilio.com"))
198+
client.Region = "region"
199+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.twilio.com"))
200+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.urlEdge.urlRegion.twilio.com"))
201+
202+
// Edge and Region set via env vars
203+
err := os.Setenv("TWILIO_EDGE", "edge")
204+
if err != nil {
205+
t.Errorf("unexpected error: %v", err)
206+
}
207+
208+
err = os.Setenv("TWILIO_REGION", "region")
209+
if err != nil {
210+
t.Errorf("unexpected error: %v", err)
211+
}
212+
213+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.twilio.com"))
214+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.urlEdge.urlRegion.twilio.com"))
215+
216+
err = os.Setenv("TWILIO_REGION", "")
217+
if err != nil {
218+
t.Errorf("unexpected error: %v", err)
219+
}
220+
221+
err = os.Setenv("TWILIO_EDGE", "")
222+
if err != nil {
223+
t.Errorf("unexpected error: %v", err)
224+
}
225+
226+
assert.Equal(t, "https://api.edge.region.twilio.com", client.BuildHost("https://api.edge.region.twilio.com"))
227+
}

framework/error/error.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package error provides the interface for Twilio specfic errors.
1+
// Package error provides the interface for Twilio specific errors.
22
package error
33

44
import (

framework/error/error_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import (
99
"github.com/twilio/twilio-go/framework/error"
1010
)
1111

12-
const errorCode = 20001
13-
const errorMessage = "Bad request"
14-
const errorMoreInfo = "https://www.twilio.com/docs/errors/20001"
15-
const errorStatus = 400
12+
const (
13+
errorCode = 20001
14+
errorMessage = "Bad request"
15+
errorMoreInfo = "https://www.twilio.com/docs/errors/20001"
16+
errorStatus = 400
17+
)
1618

1719
func TestTwilioRestError_Error(t *testing.T) {
1820
details := make(map[string]interface{})

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ module github.com/twilio/twilio-go
33
go 1.16
44

55
require (
6-
github.com/golangci/golangci-lint v1.37.0 // indirect
6+
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 // indirect
7+
github.com/golangci/golangci-lint v1.39.0 // indirect
8+
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc // indirect
79
github.com/pkg/errors v0.9.1
10+
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada // indirect
811
github.com/stretchr/testify v1.7.0
912
)

0 commit comments

Comments
 (0)