From 730522423f84236bb3f03a9bcc0b4f44ae44d934 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Mon, 19 May 2025 21:13:30 +0530 Subject: [PATCH 1/6] feat: Introduce Context --- client/client.go | 83 +++++++++++++++++++++++++++++++++++++++ client/request_handler.go | 39 ++++++++++++++++-- 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/client/client.go b/client/client.go index d4b58ace9..ea5c7a8ab 100644 --- a/client/client.go +++ b/client/client.go @@ -220,6 +220,89 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, return c.doWithErr(req) } +func (c *Client) SendRequestWithCtx(ctx context.Context, method string, rawURL string, data url.Values, + headers map[string]interface{}, body ...byte) (*http.Response, error) { + + contentType := extractContentTypeHeader(headers) + + u, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + + valueReader := &strings.Reader{} + goVersion := runtime.Version() + var req *http.Request + + //For HTTP GET Method there are no body parameters. All other parameters like query, path etc + // are added as information in the url itself. Also while Content-Type is json, we are sending + // json body. In that case, data variable contains all other parameters than body, which is the + //same case as GET method. In that case as well all parameters will be added to url + if method == http.MethodGet || method == http.MethodDelete || contentType == jsonContentType { + if data != nil { + v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros) + s := delimitingRegex.ReplaceAllString(v, "") + + u.RawQuery = s + } + } + + //data is already processed and information will be added to u(the url) in the + //previous step. Now body will solely contain json payload + if contentType == jsonContentType { + req, err = http.NewRequestWithContext(ctx, method, u.String(), bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + } else { + // Here the HTTP POST methods which do not have json content type are processed + // All the values will be added in data and encoded (all body, query, path parameters) + if method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch { + valueReader = strings.NewReader(data.Encode()) + } + req, err = http.NewRequestWithContext(context.Background(), method, u.String(), valueReader) + if err != nil { + return nil, err + } + + } + + credErr := c.validateCredentials() + if credErr != nil { + return nil, credErr + } + if c.OAuth() == nil && c.Username != "" && c.Password != "" { + req.SetBasicAuth(c.basicAuth()) + } + + // E.g. "User-Agent": "twilio-go/1.0.0 (darwin amd64) go/go1.17.8" + userAgentOnce.Do(func() { + baseUserAgent = fmt.Sprintf("twilio-go/%s (%s %s) go/%s", LibraryVersion, runtime.GOOS, runtime.GOARCH, goVersion) + }) + userAgent := baseUserAgent + + if len(c.UserAgentExtensions) > 0 { + userAgent += " " + strings.Join(c.UserAgentExtensions, " ") + } + if c.OAuth() != nil { + oauth := c.OAuth() + token, _ := c.OAuth().GetAccessToken(context.TODO()) + if token != "" { + req.Header.Add("Authorization", "Bearer "+token) + } + c.SetOauth(oauth) // Set the OAuth token in the client which gets nullified after the token fetch + } else if c.Username != "" && c.Password != "" { + req.SetBasicAuth(c.basicAuth()) + } + + req.Header.Add("User-Agent", userAgent) + + for k, v := range headers { + req.Header.Add(k, fmt.Sprint(v)) + } + return c.doWithErr(req) +} + // SetAccountSid sets the Client's accountSid field func (c *Client) SetAccountSid(sid string) { c.accountSid = sid diff --git a/client/request_handler.go b/client/request_handler.go index 5c0fdcb8e..7c9b0808b 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -2,6 +2,7 @@ package client import ( + "context" "net/http" "net/url" "os" @@ -12,6 +13,7 @@ type RequestHandler struct { Client BaseClient Edge string Region string + ctx context.Context } func NewRequestHandler(client BaseClient) *RequestHandler { @@ -22,6 +24,15 @@ func NewRequestHandler(client BaseClient) *RequestHandler { } } +func NewRequestHandlerWithCtx(client BaseClient) *RequestHandler { + return &RequestHandler{ + Client: client, + Edge: os.Getenv("TWILIO_EDGE"), + Region: os.Getenv("TWILIO_REGION"), + ctx: context.TODO(), + } +} + func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { parsedURL, err := c.BuildUrl(rawURL) @@ -83,21 +94,41 @@ func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { } func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) + return c.PostWithCtx(context.TODO(), path, bodyData, headers, body...) } func (c *RequestHandler) Put(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) + return c.PutWithCtx(context.TODO(), path, bodyData, headers, body...) } func (c *RequestHandler) Patch(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) + return c.PatchWithCtx(context.TODO(), path, bodyData, headers, body...) } func (c *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodGet, path, queryData, headers) + return c.GetWithCtx(context.TODO(), path, queryData, headers) } func (c *RequestHandler) Delete(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.DeleteWithCtx(context.TODO(), path, queryData, headers) +} + +func (c *RequestHandler) PostWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) +} + +func (c *RequestHandler) PutWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) +} + +func (c *RequestHandler) PatchWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) +} + +func (c *RequestHandler) GetWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.sendRequest(http.MethodGet, path, queryData, headers) +} + +func (c *RequestHandler) DeleteWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { return c.sendRequest(http.MethodDelete, path, queryData, headers) } From a3fd1a4e0b36cebe9c4fc61fd8650ba34c73df59 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Mon, 19 May 2025 21:54:03 +0530 Subject: [PATCH 2/6] feat: Introduce Context --- client/base_client.go | 3 +++ client/request_handler.go | 40 ++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/client/base_client.go b/client/base_client.go index 6e84a19cd..26b2c6803 100644 --- a/client/base_client.go +++ b/client/base_client.go @@ -1,6 +1,7 @@ package client import ( + "context" "net/http" "net/url" "time" @@ -13,4 +14,6 @@ type BaseClient interface { headers map[string]interface{}, body ...byte) (*http.Response, error) SetOauth(auth OAuth) OAuth() OAuth + SendRequestWithCtx(ctx context.Context, method string, rawURL string, data url.Values, + headers map[string]interface{}, body ...byte) (*http.Response, error) } diff --git a/client/request_handler.go b/client/request_handler.go index 7c9b0808b..ed701e1e0 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -13,6 +13,12 @@ type RequestHandler struct { Client BaseClient Edge string Region string +} + +type RequestHandlerWithCtx struct { + Client BaseClient + Edge string + Region string ctx context.Context } @@ -24,8 +30,8 @@ func NewRequestHandler(client BaseClient) *RequestHandler { } } -func NewRequestHandlerWithCtx(client BaseClient) *RequestHandler { - return &RequestHandler{ +func NewRequestHandlerWithCtx(client BaseClient) *RequestHandlerWithCtx { + return &RequestHandlerWithCtx{ Client: client, Edge: os.Getenv("TWILIO_EDGE"), Region: os.Getenv("TWILIO_REGION"), @@ -94,41 +100,41 @@ func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { } func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.PostWithCtx(context.TODO(), path, bodyData, headers, body...) + return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) } func (c *RequestHandler) Put(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.PutWithCtx(context.TODO(), path, bodyData, headers, body...) + return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) } func (c *RequestHandler) Patch(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.PatchWithCtx(context.TODO(), path, bodyData, headers, body...) + return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) } func (c *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.GetWithCtx(context.TODO(), path, queryData, headers) + return c.sendRequest(http.MethodGet, path, queryData, headers) } func (c *RequestHandler) Delete(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.DeleteWithCtx(context.TODO(), path, queryData, headers) + return c.sendRequest(http.MethodDelete, path, queryData, headers) } -func (c *RequestHandler) PostWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) +func (c *RequestHandlerWithCtx) PostWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithCtx(ctx, http.MethodPost, path, bodyData, headers, body...) } -func (c *RequestHandler) PutWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) +func (c *RequestHandlerWithCtx) PutWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithCtx(ctx, http.MethodPut, path, bodyData, headers, body...) } -func (c *RequestHandler) PatchWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) +func (c *RequestHandlerWithCtx) PatchWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithCtx(ctx, http.MethodPatch, path, bodyData, headers, body...) } -func (c *RequestHandler) GetWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodGet, path, queryData, headers) +func (c *RequestHandlerWithCtx) GetWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithCtx(ctx, http.MethodGet, path, queryData, headers) } -func (c *RequestHandler) DeleteWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodDelete, path, queryData, headers) +func (c *RequestHandlerWithCtx) DeleteWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithCtx(ctx, http.MethodDelete, path, queryData, headers) } From f30cccf9118a6522e4033c85aa4a9dd7802640bb Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Wed, 21 May 2025 14:25:07 +0530 Subject: [PATCH 3/6] chore: Introduce context --- client/base_client.go | 2 +- client/client.go | 2 +- client/page_util.go | 5 ++-- client/page_util_test.go | 7 +++-- client/request_handler.go | 50 ++++++++++------------------------ client/request_handler_test.go | 11 ++++---- 6 files changed, 30 insertions(+), 47 deletions(-) diff --git a/client/base_client.go b/client/base_client.go index 26b2c6803..f511985ea 100644 --- a/client/base_client.go +++ b/client/base_client.go @@ -14,6 +14,6 @@ type BaseClient interface { headers map[string]interface{}, body ...byte) (*http.Response, error) SetOauth(auth OAuth) OAuth() OAuth - SendRequestWithCtx(ctx context.Context, method string, rawURL string, data url.Values, + SendRequestWithContext(ctx context.Context, method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) } diff --git a/client/client.go b/client/client.go index ea5c7a8ab..c7a473412 100644 --- a/client/client.go +++ b/client/client.go @@ -220,7 +220,7 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, return c.doWithErr(req) } -func (c *Client) SendRequestWithCtx(ctx context.Context, method string, rawURL string, data url.Values, +func (c *Client) SendRequestWithContext(ctx context.Context, method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { contentType := extractContentTypeHeader(headers) diff --git a/client/page_util.go b/client/page_util.go index a9b722f71..0b35e8622 100644 --- a/client/page_util.go +++ b/client/page_util.go @@ -1,6 +1,7 @@ package client import ( + "context" "encoding/json" "fmt" "strings" @@ -25,13 +26,13 @@ func ReadLimits(pageSize *int, limit *int) int { } } -func GetNext(baseUrl string, response interface{}, getNextPage func(nextPageUri string) (interface{}, error)) (interface{}, error) { +func GetNextWithContext(ctx context.Context, baseUrl string, response interface{}, getNextPage func(ctx context.Context, nextPageUri string) (interface{}, error)) (interface{}, error) { nextPageUrl, err := getNextPageUrl(baseUrl, response) if err != nil { return nil, err } - return getNextPage(nextPageUrl) + return getNextPage(ctx, nextPageUrl) } func toMap(s interface{}) (map[string]interface{}, error) { diff --git a/client/page_util_test.go b/client/page_util_test.go index ebfd85509..490b1c66a 100644 --- a/client/page_util_test.go +++ b/client/page_util_test.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "context" "encoding/json" "io" "net/http" @@ -140,7 +141,7 @@ type testMessage struct { To *string `json:"to,omitempty"` } -func getSomething(nextPageUrl string) (interface{}, error) { +func getSomething(ctx context.Context, nextPageUrl string) (interface{}, error) { return nextPageUrl, nil } @@ -151,11 +152,11 @@ func TestPageUtil_GetNext(t *testing.T) { ps := &testResponse{} _ = json.NewDecoder(response.Body).Decode(ps) - nextPageUrl, err := GetNext(baseUrl, ps, getSomething) + nextPageUrl, err := GetNextWithContext(context.TODO(), baseUrl, ps, getSomething) assert.Equal(t, "https://api.twilio.com/2010-04-01/Accounts/ACXX/Messages.json?From=9999999999&PageNumber=&To=4444444444&PageSize=2&Page=1&PageToken=PASMXX", nextPageUrl) assert.Nil(t, err) - nextPageUrl, err = GetNext(baseUrl, nil, getSomething) + nextPageUrl, err = GetNextWithContext(context.TODO(), baseUrl, nil, getSomething) assert.Empty(t, nextPageUrl) assert.Nil(t, err) } diff --git a/client/request_handler.go b/client/request_handler.go index ed701e1e0..aeb6ec977 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -15,7 +15,7 @@ type RequestHandler struct { Region string } -type RequestHandlerWithCtx struct { +type RequestHandlerWithContext struct { Client BaseClient Edge string Region string @@ -30,8 +30,8 @@ func NewRequestHandler(client BaseClient) *RequestHandler { } } -func NewRequestHandlerWithCtx(client BaseClient) *RequestHandlerWithCtx { - return &RequestHandlerWithCtx{ +func NewRequestHandlerWithContext(client BaseClient) *RequestHandlerWithContext { + return &RequestHandlerWithContext{ Client: client, Edge: os.Getenv("TWILIO_EDGE"), Region: os.Getenv("TWILIO_REGION"), @@ -39,7 +39,7 @@ func NewRequestHandlerWithCtx(client BaseClient) *RequestHandlerWithCtx { } } -func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Values, +func (c *RequestHandlerWithContext) sendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { parsedURL, err := c.BuildUrl(rawURL) if err != nil { @@ -49,7 +49,7 @@ func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Valu } // BuildUrl builds the target host string taking into account region and edge configurations. -func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { +func (c *RequestHandlerWithContext) BuildUrl(rawURL string) (string, error) { u, err := url.Parse(rawURL) if err != nil { return "", err @@ -99,42 +99,22 @@ func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { return u.String(), nil } -func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPost, path, bodyData, headers, body...) +func (c *RequestHandlerWithContext) PostWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPost, path, bodyData, headers, body...) } -func (c *RequestHandler) Put(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPut, path, bodyData, headers, body...) +func (c *RequestHandlerWithContext) PutWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPut, path, bodyData, headers, body...) } -func (c *RequestHandler) Patch(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.sendRequest(http.MethodPatch, path, bodyData, headers, body...) +func (c *RequestHandlerWithContext) PatchWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodPatch, path, bodyData, headers, body...) } -func (c *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodGet, path, queryData, headers) +func (c *RequestHandlerWithContext) GetWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodGet, path, queryData, headers) } -func (c *RequestHandler) Delete(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.sendRequest(http.MethodDelete, path, queryData, headers) -} - -func (c *RequestHandlerWithCtx) PostWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.Client.SendRequestWithCtx(ctx, http.MethodPost, path, bodyData, headers, body...) -} - -func (c *RequestHandlerWithCtx) PutWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.Client.SendRequestWithCtx(ctx, http.MethodPut, path, bodyData, headers, body...) -} - -func (c *RequestHandlerWithCtx) PatchWithCtx(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - return c.Client.SendRequestWithCtx(ctx, http.MethodPatch, path, bodyData, headers, body...) -} - -func (c *RequestHandlerWithCtx) GetWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.Client.SendRequestWithCtx(ctx, http.MethodGet, path, queryData, headers) -} - -func (c *RequestHandlerWithCtx) DeleteWithCtx(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { - return c.Client.SendRequestWithCtx(ctx, http.MethodDelete, path, queryData, headers) +func (c *RequestHandlerWithContext) DeleteWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { + return c.Client.SendRequestWithContext(ctx, http.MethodDelete, path, queryData, headers) } diff --git a/client/request_handler_test.go b/client/request_handler_test.go index 1756ae521..1b1710132 100644 --- a/client/request_handler_test.go +++ b/client/request_handler_test.go @@ -1,6 +1,7 @@ package client_test import ( + "context" "errors" "net/http" "net/http/httptest" @@ -11,9 +12,9 @@ import ( "github.com/twilio/twilio-go/client" ) -func NewRequestHandler(accountSid string, authToken string) *client.RequestHandler { +func NewRequestHandler(accountSid string, authToken string) *client.RequestHandlerWithContext { c := NewClient(accountSid, authToken) - return client.NewRequestHandler(c) + return client.NewRequestHandlerWithContext(c) } func TestRequestHandler_BuildUrlSetRegion(t *testing.T) { @@ -62,7 +63,7 @@ func TestRequestHandler_BuildUrlInvalidCTLCharacter(t *testing.T) { assert.Equal(t, parsedURL, "") } -func assertAndGetURL(t *testing.T, requestHandler *client.RequestHandler, rawURL string) string { +func assertAndGetURL(t *testing.T, requestHandler *client.RequestHandlerWithContext, rawURL string) string { parsedURL, err := requestHandler.BuildUrl(rawURL) assert.Nil(t, err) return parsedURL @@ -83,7 +84,7 @@ func TestRequestHandler_SendGetRequest(t *testing.T) { defer errorServer.Close() requestHandler := NewRequestHandler("user", "pass") - resp, err := requestHandler.Get(errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := requestHandler.GetWithContext(context.TODO(), errorServer.URL, nil, nil) //nolint:bodyclose twilioError := err.(*client.TwilioRestError) assert.Nil(t, resp) assert.Equal(t, 400, twilioError.Status) @@ -108,7 +109,7 @@ func TestRequestHandler_SendPostRequest(t *testing.T) { defer errorServer.Close() requestHandler := NewRequestHandler("user", "pass") - resp, err := requestHandler.Post(errorServer.URL, nil, nil) //nolint:bodyclose + resp, err := requestHandler.PostWithContext(context.TODO(), errorServer.URL, nil, nil) //nolint:bodyclose twilioError := err.(*client.TwilioRestError) assert.Nil(t, resp) assert.Equal(t, 400, twilioError.Status) From 34b0dc383bcdc5aa367d7bf985d56f210bb6355b Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Wed, 21 May 2025 14:30:14 +0530 Subject: [PATCH 4/6] chore: Introduce context --- client/client.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/client.go b/client/client.go index c7a473412..6763d11fb 100644 --- a/client/client.go +++ b/client/client.go @@ -21,6 +21,8 @@ import ( var alphanumericRegex *regexp.Regexp var delimitingRegex *regexp.Regexp +var goVersion string +var goVersionOnce sync.Once func init() { alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`) @@ -148,7 +150,7 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, } valueReader := &strings.Reader{} - goVersion := runtime.Version() + goVersion = getGoVersion() var req *http.Request //For HTTP GET Method there are no body parameters. All other parameters like query, path etc @@ -320,3 +322,10 @@ func (c *Client) SetOauth(oauth OAuth) { func (c *Client) OAuth() OAuth { return c.oAuth } + +func getGoVersion() string { + goVersionOnce.Do(func() { + goVersion = runtime.Version() + }) + return goVersion +} From 78ccbce947a1b897d02d6ac541ea9c0c4091d3b1 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Fri, 30 May 2025 12:30:54 +0530 Subject: [PATCH 5/6] chore: add context to request handler --- client/request_handler.go | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/client/request_handler.go b/client/request_handler.go index aeb6ec977..71b531c8a 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -13,12 +13,6 @@ type RequestHandler struct { Client BaseClient Edge string Region string -} - -type RequestHandlerWithContext struct { - Client BaseClient - Edge string - Region string ctx context.Context } @@ -27,19 +21,11 @@ func NewRequestHandler(client BaseClient) *RequestHandler { Client: client, Edge: os.Getenv("TWILIO_EDGE"), Region: os.Getenv("TWILIO_REGION"), - } -} - -func NewRequestHandlerWithContext(client BaseClient) *RequestHandlerWithContext { - return &RequestHandlerWithContext{ - Client: client, - Edge: os.Getenv("TWILIO_EDGE"), - Region: os.Getenv("TWILIO_REGION"), ctx: context.TODO(), } } -func (c *RequestHandlerWithContext) sendRequest(method string, rawURL string, data url.Values, +func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { parsedURL, err := c.BuildUrl(rawURL) if err != nil { @@ -49,7 +35,7 @@ func (c *RequestHandlerWithContext) sendRequest(method string, rawURL string, da } // BuildUrl builds the target host string taking into account region and edge configurations. -func (c *RequestHandlerWithContext) BuildUrl(rawURL string) (string, error) { +func (c *RequestHandler) BuildUrl(rawURL string) (string, error) { u, err := url.Parse(rawURL) if err != nil { return "", err @@ -99,22 +85,22 @@ func (c *RequestHandlerWithContext) BuildUrl(rawURL string) (string, error) { return u.String(), nil } -func (c *RequestHandlerWithContext) PostWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { +func (c *RequestHandler) PostWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { return c.Client.SendRequestWithContext(ctx, http.MethodPost, path, bodyData, headers, body...) } -func (c *RequestHandlerWithContext) PutWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { +func (c *RequestHandler) PutWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { return c.Client.SendRequestWithContext(ctx, http.MethodPut, path, bodyData, headers, body...) } -func (c *RequestHandlerWithContext) PatchWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { +func (c *RequestHandler) PatchWithContext(ctx context.Context, path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { return c.Client.SendRequestWithContext(ctx, http.MethodPatch, path, bodyData, headers, body...) } -func (c *RequestHandlerWithContext) GetWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { +func (c *RequestHandler) GetWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { return c.Client.SendRequestWithContext(ctx, http.MethodGet, path, queryData, headers) } -func (c *RequestHandlerWithContext) DeleteWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { +func (c *RequestHandler) DeleteWithContext(ctx context.Context, path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) { return c.Client.SendRequestWithContext(ctx, http.MethodDelete, path, queryData, headers) } From 3f9b6c2f28b020a81b8c68a55e2e15fc6421861e Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Fri, 30 May 2025 12:42:48 +0530 Subject: [PATCH 6/6] chore: add context to request handler --- client/client.go | 81 ++---------------------------------------------- 1 file changed, 2 insertions(+), 79 deletions(-) diff --git a/client/client.go b/client/client.go index 6763d11fb..f4ff8019b 100644 --- a/client/client.go +++ b/client/client.go @@ -142,84 +142,7 @@ var userAgentOnce sync.Once func (c *Client) SendRequest(method string, rawURL string, data url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) { - contentType := extractContentTypeHeader(headers) - - u, err := url.Parse(rawURL) - if err != nil { - return nil, err - } - - valueReader := &strings.Reader{} - goVersion = getGoVersion() - var req *http.Request - - //For HTTP GET Method there are no body parameters. All other parameters like query, path etc - // are added as information in the url itself. Also while Content-Type is json, we are sending - // json body. In that case, data variable contains all other parameters than body, which is the - //same case as GET method. In that case as well all parameters will be added to url - if method == http.MethodGet || method == http.MethodDelete || contentType == jsonContentType { - if data != nil { - v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros) - s := delimitingRegex.ReplaceAllString(v, "") - - u.RawQuery = s - } - } - - //data is already processed and information will be added to u(the url) in the - //previous step. Now body will solely contain json payload - if contentType == jsonContentType { - req, err = http.NewRequest(method, u.String(), bytes.NewBuffer(body)) - if err != nil { - return nil, err - } - } else { - // Here the HTTP POST methods which do not have json content type are processed - // All the values will be added in data and encoded (all body, query, path parameters) - if method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch { - valueReader = strings.NewReader(data.Encode()) - } - req, err = http.NewRequestWithContext(context.Background(), method, u.String(), valueReader) - if err != nil { - return nil, err - } - - } - - credErr := c.validateCredentials() - if credErr != nil { - return nil, credErr - } - if c.OAuth() == nil && c.Username != "" && c.Password != "" { - req.SetBasicAuth(c.basicAuth()) - } - - // E.g. "User-Agent": "twilio-go/1.0.0 (darwin amd64) go/go1.17.8" - userAgentOnce.Do(func() { - baseUserAgent = fmt.Sprintf("twilio-go/%s (%s %s) go/%s", LibraryVersion, runtime.GOOS, runtime.GOARCH, goVersion) - }) - userAgent := baseUserAgent - - if len(c.UserAgentExtensions) > 0 { - userAgent += " " + strings.Join(c.UserAgentExtensions, " ") - } - if c.OAuth() != nil { - oauth := c.OAuth() - token, _ := c.OAuth().GetAccessToken(context.TODO()) - if token != "" { - req.Header.Add("Authorization", "Bearer "+token) - } - c.SetOauth(oauth) // Set the OAuth token in the client which gets nullified after the token fetch - } else if c.Username != "" && c.Password != "" { - req.SetBasicAuth(c.basicAuth()) - } - - req.Header.Add("User-Agent", userAgent) - - for k, v := range headers { - req.Header.Add(k, fmt.Sprint(v)) - } - return c.doWithErr(req) + return c.SendRequestWithContext(context.TODO(), method, rawURL, data, headers, body...) } func (c *Client) SendRequestWithContext(ctx context.Context, method string, rawURL string, data url.Values, @@ -288,7 +211,7 @@ func (c *Client) SendRequestWithContext(ctx context.Context, method string, rawU } if c.OAuth() != nil { oauth := c.OAuth() - token, _ := c.OAuth().GetAccessToken(context.TODO()) + token, _ := c.OAuth().GetAccessToken(ctx) if token != "" { req.Header.Add("Authorization", "Bearer "+token) }