Skip to content

Commit fdee56a

Browse files
feat: added support for application/json content type in request body (#239)
* feat: added support for application/json content type in request body * docs: updated README.md with feature update * chore: corrected version * chore: corrected model names * chore: added messaging bulk example (#243) * chore: added messaging bulk example * chore: added messaging bulk example * chore: added messaging bulk example * chore: corrected message
1 parent 298c07d commit fdee56a

File tree

607 files changed

+5905
-797
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

607 files changed

+5905
-797
lines changed

Diff for: README.md

+47-1
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
[![Tests](https://github.com/twilio/twilio-go/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/twilio/twilio-go/actions/workflows/test-and-deploy.yml)
44
[![PkgGoDev](https://pkg.go.dev/badge/github.com/twilio/twilio-go)](https://pkg.go.dev/github.com/twilio/twilio-go)
55
[![Release](https://img.shields.io/github/release/twilio/twilio-go.svg)](https://github.com/twilio/twilio-go/releases/latest)
6-
[![Learn OSS Contribution in TwilioQuest](https://img.shields.io/static/v1?label=TwilioQuest&message=Learn%20to%20contribute%21&color=F22F46&labelColor=1f243c&style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAASFBMVEUAAAAZGRkcHBwjIyMoKCgAAABgYGBoaGiAgICMjIyzs7PJycnMzMzNzc3UoBfd3d3m5ubqrhfrMEDu7u739/f4vSb/3AD///9tbdyEAAAABXRSTlMAAAAAAMJrBrEAAAKoSURBVHgB7ZrRcuI6EESdyxXGYoNFvMD//+l2bSszRgyUYpFAsXOeiJGmj4NkuWx1Qeh+Ekl9DgEXOBwOx+Px5xyQhDykfgq4wG63MxxaR4ddIkg6Ul3g84vCIcjPBA5gmUMeXESrlukuoK33+33uID8TWeLAdOWsKpJYzwVMB7bOzYSGOciyUlXSn0/ABXTosJ1M1SbypZ4O4MbZuIDMU02PMbauhhHMHXbmebmALIiEbbbbbUrpF1gwE9kFfRNAJaP+FQEXCCTGyJ4ngDrjOFo3jEL5JdqjF/pueR4cCeCGgAtwmuRS6gDwaRiGvu+DMFwSBLTE3+jF8JyuV1okPZ+AC4hDFhCHyHQjdjPHUKFDlHSJkHQXMB3KpSwXNGJPcwwTdZiXlRN0gSp0zpWxNtM0beYE0nRH6QIbO7rawwXaBYz0j78gxjokDuv12gVeUuBD0MDi0OQCLvDaAho4juP1Q/jkAncXqIcCfd+7gAu4QLMACCLxpRsSuQh0igu0C9Svhi7weAGZg50L3IE3cai4IfkNZAC8dfdhsUD3CgKBVC9JE5ABAFzg4QL/taYPAAWrHdYcgfLaIgAXWJ7OV38n1LEF8tt2TH29E+QAoDoO5Ve/LtCQDmKM9kPbvCEBApK+IXzbcSJ0cIGF6e8gpcRhUDogWZ8JnaWjPXc/fNnBBUKRngiHgTUSivSzDRDgHZQOLvBQgf8rRt+VdBUUhwkU6VpJ+xcOwQUqZr+mR0kvBUgv6cB4+37hQAkXqE8PwGisGhJtN4xAHMzrsgvI7rccXqSvKh6jltGlrOHA3Xk1At3LC4QiPdX9/0ndHpGVvTjR4bZA1ypAKgVcwE5vx74ulwIugDt8e/X7JgfkucBMIAr26ndnB4UCLnDOqvteQsHlgX9N4A+c4cW3DXSPbwAAAABJRU5ErkJggg==)](https://twil.io/learn-open-source)
76

87
All the code [here](./rest) was generated by [twilio-oai-generator](https://github.com/twilio/twilio-oai-generator) by
98
leveraging [openapi-generator](https://github.com/OpenAPITools/openapi-generator)
109
and [twilio-oai](https://github.com/twilio/twilio-oai). If you find an issue with the generation or the OpenAPI specs,
1110
please go ahead and open an issue or a PR against the relevant repositories.
1211

12+
## 🚀 Feature Update
13+
Twilio Go Helper Library's version 1.20.0 adds support for the application/json content type in the request body. See example [here](#messaging-bulk).
14+
Behind the scenes Go Helper is now auto-generated via OpenAPI with this release.
15+
This enables us to rapidly add new features and enhance consistency across versions and languages.
16+
1317
## Documentation
1418

1519
The documentation for the Twilio API can be found [here][apidocs].
@@ -297,6 +301,48 @@ func main() {
297301
}
298302
```
299303

304+
### Send Bulk Message <a id="messaging-bulk"></a>
305+
306+
Try sending a message to multiple recipients with JSON request body support.
307+
308+
```go
309+
package main
310+
311+
import (
312+
"encoding/json"
313+
"fmt"
314+
315+
"github.com/twilio/twilio-go"
316+
previewMessaging "github.com/twilio/twilio-go/rest/preview_messaging/v1"
317+
)
318+
319+
func main() {
320+
accountSid := "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
321+
authToken := "f2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
322+
323+
client := twilio.NewRestClientWithParams(twilio.ClientParams{
324+
Username: accountSid,
325+
Password: authToken,
326+
})
327+
328+
// create multiple recipients
329+
msg1 := previewMessaging.MessagingV1Message{To: "+XXXXXXXXXX"}
330+
msg2 := previewMessaging.MessagingV1Message{To: "+XXXXXXXXXX"}
331+
332+
// create message request object
333+
req := &previewMessaging.CreateMessagesRequest{Messages: []previewMessaging.MessagingV1Message{msg1, msg2}, Body: "Hello from Go!", From: "+XXXXXXXXXX"}
334+
params := &previewMessaging.CreateMessagesParams{CreateMessagesRequest: req}
335+
336+
resp, err := client.PreviewMessagingV1.CreateMessages(params)
337+
if err != nil {
338+
fmt.Println("Error sending SMS message: " + err.Error())
339+
} else {
340+
response, _ := json.Marshal(*resp)
341+
fmt.Println("Response: " + string(response))
342+
}
343+
}
344+
```
345+
300346
### Iterate through records
301347

302348
This library also offers paging functionality. Collections such as calls and messages have `ListXxx` and `StreamXxx`

Diff for: client/base_client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ type BaseClient interface {
1010
AccountSid() string
1111
SetTimeout(timeout time.Duration)
1212
SendRequest(method string, rawURL string, data url.Values,
13-
headers map[string]interface{}) (*http.Response, error)
13+
headers map[string]interface{}, body ...byte) (*http.Response, error)
1414
}

Diff for: client/client.go

+47-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package client
33

44
import (
5+
"bytes"
56
"encoding/json"
67
"fmt"
78
"net/http"
@@ -64,10 +65,20 @@ func (c *Client) SetTimeout(timeout time.Duration) {
6465
c.HTTPClient.Timeout = timeout
6566
}
6667

68+
func extractContentTypeHeader(headers map[string]interface{}) (cType string) {
69+
headerType, ok := headers["Content-Type"]
70+
if !ok {
71+
return urlEncodedContentType
72+
}
73+
return headerType.(string)
74+
}
75+
6776
const (
68-
keepZeros = true
69-
delimiter = '.'
70-
escapee = '\\'
77+
urlEncodedContentType = "application/x-www-form-urlencoded"
78+
jsonContentType = "application/json"
79+
keepZeros = true
80+
delimiter = '.'
81+
escapee = '\\'
7182
)
7283

7384
func (c *Client) doWithErr(req *http.Request) (*http.Response, error) {
@@ -117,16 +128,24 @@ func (c *Client) validateCredentials() error {
117128

118129
// SendRequest verifies, constructs, and authorizes an HTTP request.
119130
func (c *Client) SendRequest(method string, rawURL string, data url.Values,
120-
headers map[string]interface{}) (*http.Response, error) {
131+
headers map[string]interface{}, body ...byte) (*http.Response, error) {
132+
133+
contentType := extractContentTypeHeader(headers)
134+
121135
u, err := url.Parse(rawURL)
122136
if err != nil {
123137
return nil, err
124138
}
125139

126140
valueReader := &strings.Reader{}
127141
goVersion := runtime.Version()
142+
var req *http.Request
128143

129-
if method == http.MethodGet {
144+
//For HTTP GET Method there are no body parameters. All other parameters like query, path etc
145+
// are added as information in the url itself. Also while Content-Type is json, we are sending
146+
// json body. In that case, data variable conatins all other parameters than body, which is the
147+
//same case as GET method. In that case as well all parameters will be added to url
148+
if method == http.MethodGet || contentType == jsonContentType {
130149
if data != nil {
131150
v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros)
132151
s := delimitingRegex.ReplaceAllString(v, "")
@@ -135,18 +154,32 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values,
135154
}
136155
}
137156

138-
if method == http.MethodPost {
139-
valueReader = strings.NewReader(data.Encode())
140-
}
157+
//data is already processed and information will be added to u(the url) in the
158+
//previous step. Now body will solely contain json payload
159+
if contentType == jsonContentType {
160+
req, err = http.NewRequest(method, u.String(), bytes.NewBuffer(body))
161+
if err != nil {
162+
return nil, err
163+
}
164+
} else {
165+
//Here the HTTP POST methods which is not having json content type are processed
166+
//All the values will be added in data and encoded (all body, query, path parameters)
167+
if method == http.MethodPost {
168+
valueReader = strings.NewReader(data.Encode())
169+
}
170+
credErr := c.validateCredentials()
171+
if credErr != nil {
172+
return nil, credErr
173+
}
174+
req, err = http.NewRequest(method, u.String(), valueReader)
175+
if err != nil {
176+
return nil, err
177+
}
141178

142-
credErr := c.validateCredentials()
143-
if credErr != nil {
144-
return nil, credErr
145179
}
146180

147-
req, err := http.NewRequest(method, u.String(), valueReader)
148-
if err != nil {
149-
return nil, err
181+
if contentType == urlEncodedContentType {
182+
req.Header.Add("Content-Type", urlEncodedContentType)
150183
}
151184

152185
req.SetBasicAuth(c.basicAuth())
@@ -160,14 +193,9 @@ func (c *Client) SendRequest(method string, rawURL string, data url.Values,
160193

161194
req.Header.Add("User-Agent", userAgent)
162195

163-
if method == http.MethodPost {
164-
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
165-
}
166-
167196
for k, v := range headers {
168197
req.Header.Add(k, fmt.Sprint(v))
169198
}
170-
171199
return c.doWithErr(req)
172200
}
173201

Diff for: client/client_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func TestClient_SetAccountSid(t *testing.T) {
294294
func TestClient_DefaultUserAgentHeaders(t *testing.T) {
295295
headerServer := httptest.NewServer(http.HandlerFunc(
296296
func(writer http.ResponseWriter, request *http.Request) {
297-
assert.Regexp(t, regexp.MustCompile(`^twilio-go/[0-9.]+\s\(\w+\s\w+\)\sgo/\S+$`), request.Header.Get("User-Agent"))
297+
assert.Regexp(t, regexp.MustCompile(`^twilio-go/[0-9.]+(-rc.[0-9])*\s\(\w+\s\w+\)\sgo/\S+$`), request.Header.Get("User-Agent"))
298298
}))
299299

300300
resp, _ := testClient.SendRequest("GET", headerServer.URL, nil, nil)

Diff for: client/request_handler.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ func NewRequestHandler(client BaseClient) *RequestHandler {
2323
}
2424

2525
func (c *RequestHandler) sendRequest(method string, rawURL string, data url.Values,
26-
headers map[string]interface{}) (*http.Response, error) {
26+
headers map[string]interface{}, body ...byte) (*http.Response, error) {
2727
parsedURL, err := c.BuildUrl(rawURL)
2828
if err != nil {
2929
return nil, err
3030
}
31-
32-
return c.Client.SendRequest(method, parsedURL, data, headers)
31+
return c.Client.SendRequest(method, parsedURL, data, headers, body...)
3332
}
3433

3534
// 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) {
8382
return u.String(), nil
8483
}
8584

86-
func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}) (*http.Response, error) {
87-
return c.sendRequest(http.MethodPost, path, bodyData, headers)
85+
func (c *RequestHandler) Post(path string, bodyData url.Values, headers map[string]interface{}, body ...byte) (*http.Response, error) {
86+
return c.sendRequest(http.MethodPost, path, bodyData, headers, body...)
8887
}
8988

9089
func (c *RequestHandler) Get(path string, queryData url.Values, headers map[string]interface{}) (*http.Response, error) {

Diff for: client/request_handler_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package client_test
22

33
import (
44
"errors"
5+
"net/http"
6+
"net/http/httptest"
57
"net/url"
68
"testing"
79

@@ -65,3 +67,53 @@ func assertAndGetURL(t *testing.T, requestHandler *client.RequestHandler, rawURL
6567
assert.Nil(t, err)
6668
return parsedURL
6769
}
70+
71+
func TestRequestHandler_SendGetRequest(t *testing.T) {
72+
errorResponse := `{
73+
"status": 400,
74+
"code":20001,
75+
"message":"Bad request",
76+
"more_info":"https://www.twilio.com/docs/errors/20001"
77+
}`
78+
errorServer := httptest.NewServer(http.HandlerFunc(
79+
func(resp http.ResponseWriter, req *http.Request) {
80+
resp.WriteHeader(400)
81+
_, _ = resp.Write([]byte(errorResponse))
82+
}))
83+
defer errorServer.Close()
84+
85+
requestHandler := NewRequestHandler("user", "pass")
86+
resp, err := requestHandler.Get(errorServer.URL, nil, nil) //nolint:bodyclose
87+
twilioError := err.(*client.TwilioRestError)
88+
assert.Nil(t, resp)
89+
assert.Equal(t, 400, twilioError.Status)
90+
assert.Equal(t, 20001, twilioError.Code)
91+
assert.Equal(t, "https://www.twilio.com/docs/errors/20001", twilioError.MoreInfo)
92+
assert.Equal(t, "Bad request", twilioError.Message)
93+
assert.Nil(t, twilioError.Details)
94+
}
95+
96+
func TestRequestHandler_SendPostRequest(t *testing.T) {
97+
errorResponse := `{
98+
"status": 400,
99+
"code":20001,
100+
"message":"Bad request",
101+
"more_info":"https://www.twilio.com/docs/errors/20001"
102+
}`
103+
errorServer := httptest.NewServer(http.HandlerFunc(
104+
func(resp http.ResponseWriter, req *http.Request) {
105+
resp.WriteHeader(400)
106+
_, _ = resp.Write([]byte(errorResponse))
107+
}))
108+
defer errorServer.Close()
109+
110+
requestHandler := NewRequestHandler("user", "pass")
111+
resp, err := requestHandler.Post(errorServer.URL, nil, nil) //nolint:bodyclose
112+
twilioError := err.(*client.TwilioRestError)
113+
assert.Nil(t, resp)
114+
assert.Equal(t, 400, twilioError.Status)
115+
assert.Equal(t, 20001, twilioError.Code)
116+
assert.Equal(t, "https://www.twilio.com/docs/errors/20001", twilioError.MoreInfo)
117+
assert.Equal(t, "Bad request", twilioError.Message)
118+
assert.Nil(t, twilioError.Details)
119+
}

Diff for: rest/accounts/v1/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This is the public Twilio REST API.
55
## Overview
66
This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project from the OpenAPI specs located at [twilio/twilio-oai](https://github.com/twilio/twilio-oai/tree/main/spec). By using the [OpenAPI-spec](https://www.openapis.org/) from a remote server, you can easily generate an API client.
77

8-
- API version: 1.55.3
8+
- API version: 1.0.0
99
- Package version: 1.0.0
1010
- Build package: com.twilio.oai.TwilioGoGenerator
1111
For more information, please visit [https://support.twilio.com](https://support.twilio.com)

Diff for: rest/accounts/v1/docs/ListCredentialAwsResponseMeta.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
Name | Type | Description | Notes
66
------------ | ------------- | ------------- | -------------
77
**FirstPageUrl** | **string** | |[optional]
8+
**Key** | **string** | |[optional]
89
**NextPageUrl** | Pointer to **string** | |
910
**Page** | **int** | |[optional]
1011
**PageSize** | **int** | |[optional]
1112
**PreviousPageUrl** | Pointer to **string** | |
1213
**Url** | **string** | |[optional]
13-
**Key** | **string** | |[optional]
1414

1515
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
1616

Diff for: rest/accounts/v1/model_list_credential_aws_response_meta.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ package openapi
1717
// ListCredentialAwsResponseMeta struct for ListCredentialAwsResponseMeta
1818
type ListCredentialAwsResponseMeta struct {
1919
FirstPageUrl string `json:"first_page_url,omitempty"`
20+
Key string `json:"key,omitempty"`
2021
NextPageUrl *string `json:"next_page_url,omitempty"`
2122
Page int `json:"page,omitempty"`
2223
PageSize int `json:"page_size,omitempty"`
2324
PreviousPageUrl *string `json:"previous_page_url,omitempty"`
2425
Url string `json:"url,omitempty"`
25-
Key string `json:"key,omitempty"`
2626
}

0 commit comments

Comments
 (0)