From 6548a8dcb74518d0525a5d95f1259e382c918a5d Mon Sep 17 00:00:00 2001 From: Athira Sabu <102021496+AsabuHere@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:03:20 +0530 Subject: [PATCH] feat: Support JSON payload in HTTP requests (#213) * Add support for setting Content-Type header to application/json * Add support for serialization and deserialization of json payload * Modified json ingress code changes with variadic functions --- client/base_client.go | 2 +- client/client.go | 58 +++++++++++++++++++++++++++++---------- client/request_handler.go | 9 +++--- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/client/base_client.go b/client/base_client.go index 974560dcd..4febc438f 100644 --- a/client/base_client.go +++ b/client/base_client.go @@ -10,5 +10,5 @@ type BaseClient interface { AccountSid() string SetTimeout(timeout time.Duration) SendRequest(method string, rawURL string, data url.Values, - headers map[string]interface{}) (*http.Response, error) + headers map[string]interface{}, body ...byte) (*http.Response, error) } diff --git a/client/client.go b/client/client.go index b7b8f874a..0a5d20ec7 100644 --- a/client/client.go +++ b/client/client.go @@ -5,13 +5,13 @@ import ( "encoding/json" "fmt" "net/http" + "bytes" "net/url" "regexp" "runtime" "strconv" "strings" "time" - "github.com/pkg/errors" "github.com/twilio/twilio-go/client/form" ) @@ -56,7 +56,17 @@ func (c *Client) SetTimeout(timeout time.Duration) { c.HTTPClient.Timeout = timeout } +func extractContentTypeHeader(headers map[string]interface{}) (cType string){ + headerType, ok := headers["Content-Type"] + if !ok { + return urlEncodedContentType + } + return headerType.(string) +} + const ( + urlEncodedContentType = "application/x-www-form-urlencoded" + jsonContentType = "application/json" keepZeros = true delimiter = '.' escapee = '\\' @@ -89,16 +99,24 @@ func (c *Client) doWithErr(req *http.Request) (*http.Response, error) { // SendRequest verifies, constructs, and authorizes an HTTP request. func (c *Client) SendRequest(method string, rawURL string, data url.Values, - headers map[string]interface{}) (*http.Response, error) { + 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 - if method == http.MethodGet { + //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 conatins 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 || contentType == jsonContentType{ if data != nil { v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros) regex := regexp.MustCompile(`\.\d+`) @@ -108,13 +126,28 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, } } - if method == http.MethodPost { - valueReader = strings.NewReader(data.Encode()) + //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 is not having json content type are processed + //All the values will be added in data and encoded (all body, query, path parameters) + if method == http.MethodPost { + valueReader = strings.NewReader(data.Encode()) + } + req, err = http.NewRequest(method, u.String(), valueReader) + if err != nil { + return nil, err + } + } - req, err := http.NewRequest(method, u.String(), valueReader) - if err != nil { - return nil, err + if contentType == urlEncodedContentType{ + req.Header.Add("Content-Type", urlEncodedContentType) } req.SetBasicAuth(c.basicAuth()) @@ -128,14 +161,9 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values, req.Header.Add("User-Agent", userAgent) - if method == http.MethodPost { - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - } - for k, v := range headers { req.Header.Add(k, fmt.Sprint(v)) } - return c.doWithErr(req) } @@ -147,4 +175,4 @@ func (c *Client) SetAccountSid(sid string) { // Returns the Account SID. func (c *Client) AccountSid() string { return c.accountSid -} +} \ No newline at end of file diff --git a/client/request_handler.go b/client/request_handler.go index 89fe7883f..3b918e9ba 100644 --- a/client/request_handler.go +++ b/client/request_handler.go @@ -23,13 +23,12 @@ func NewRequestHandler(client BaseClient) *RequestHandler { } func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Values, - headers map[string]interface{}) (*http.Response, error) { + headers map[string]interface{}, body ...byte) (*http.Response, error) { parsedURL, err := c.BuildUrl(rawURL) if err != nil { return nil, err } - - return c.Client.SendRequest(method, parsedURL, data, headers) + return c.Client.SendRequest(method, parsedURL, data, headers, body...) } // BuildUrl builds the target host string taking into account region and edge configurations. @@ -83,8 +82,8 @@ 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{}) (*http.Response, error) { - return c.sendRequest(http.MethodPost, path, bodyData, headers) +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 *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) {