Skip to content

Commit de37532

Browse files
romanyxpolyfloyd
authored andcommitted
Deprecate ErrResponse error with ErrorResponse instance (#35)
1 parent a194ff2 commit de37532

15 files changed

+220
-55
lines changed

balance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ type Balance struct {
55
Payment string
66
Type string
77
Amount float32
8-
Errors []Error
8+
Errors []Error // Deprecated: errors now returned at ErrorResponse instance as error.
99
}

balance_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,22 @@ func TestBalance(t *testing.T) {
3535

3636
func TestBalanceError(t *testing.T) {
3737
SetServerResponse(405, accessKeyErrorObject)
38+
_, err := mbClient.Balance()
3839

39-
balance, err := mbClient.Balance()
40-
if err != ErrResponse {
41-
t.Fatalf("Expected ErrResponse to be returned, instead I got %s", err)
40+
errorResponse, ok := err.(ErrorResponse)
41+
if !ok {
42+
t.Fatalf("Expected ErrorResponse to be returned, instead I got %s", err)
4243
}
4344

44-
if len(balance.Errors) != 1 {
45-
t.Fatalf("Unexpected number of errors: %d", len(balance.Errors))
45+
if len(errorResponse.Errors) != 1 {
46+
t.Fatalf("Unexpected number of errors: %d, expected: 1", len(errorResponse.Errors))
4647
}
4748

48-
if balance.Errors[0].Code != 2 {
49-
t.Errorf("Unexpected error code: %d", balance.Errors[0].Code)
49+
if errorResponse.Errors[0].Code != 2 {
50+
t.Errorf("Unexpected error code: %d, expected: 2", errorResponse.Errors[0].Code)
5051
}
5152

52-
if balance.Errors[0].Parameter != "access_key" {
53-
t.Errorf("Unexpected error parameter: %s", balance.Errors[0].Parameter)
53+
if errorResponse.Errors[0].Parameter != "access_key" {
54+
t.Errorf("Unexpected error parameter: %s, expected: access_key", errorResponse.Errors[0].Parameter)
5455
}
5556
}

client.go

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,44 @@ type Client struct {
6363
AccessKey string // The API access key
6464
HTTPClient *http.Client // The HTTP client to send requests on
6565
DebugLog *log.Logger // Optional logger for debugging purposes
66+
request func(c *Client, v interface{}, method, path string, data interface{}) error
67+
}
68+
69+
// NewV2 creates a new MessageBird client object.
70+
// If the request to MessageBird API will return an
71+
// error, it will be returned as ErrorResponse instance
72+
// typed as the error, containing all codes,
73+
// descriptions and properies.
74+
func NewV2(accessKey string) *Client {
75+
return &Client{
76+
AccessKey: accessKey,
77+
HTTPClient: &http.Client{
78+
Timeout: httpClientTimeout,
79+
},
80+
request: requestV2,
81+
}
6682
}
6783

6884
// New creates a new MessageBird client object.
85+
// If the request to MessageBird API will return
86+
// and error, it will simply return ErrResponse.
87+
// Deprecated: use NewV2 until v2 release.
6988
func New(accessKey string) *Client {
7089
return &Client{
7190
AccessKey: accessKey,
7291
HTTPClient: &http.Client{
7392
Timeout: httpClientTimeout,
7493
},
94+
request: requestV1,
7595
}
7696
}
7797

7898
// Request is for internal use only and unstable.
7999
func (c *Client) Request(v interface{}, method, path string, data interface{}) error {
100+
return c.request(c, v, method, path, data)
101+
}
102+
103+
func requestV1(c *Client, v interface{}, method, path string, data interface{}) error {
80104
if !strings.HasPrefix(path, "https://") && !strings.HasPrefix(path, "http://") {
81105
path = fmt.Sprintf("%s/%s", Endpoint, path)
82106
}
@@ -150,6 +174,80 @@ func (c *Client) Request(v interface{}, method, path string, data interface{}) e
150174
return ErrResponse
151175
}
152176

177+
func requestV2(c *Client, v interface{}, method, path string, data interface{}) error {
178+
if !strings.HasPrefix(path, "https://") && !strings.HasPrefix(path, "http://") {
179+
path = fmt.Sprintf("%s/%s", Endpoint, path)
180+
}
181+
uri, err := url.Parse(path)
182+
if err != nil {
183+
return err
184+
}
185+
186+
var jsonEncoded []byte
187+
if data != nil {
188+
jsonEncoded, err = json.Marshal(data)
189+
if err != nil {
190+
return err
191+
}
192+
}
193+
194+
request, err := http.NewRequest(method, uri.String(), bytes.NewBuffer(jsonEncoded))
195+
if err != nil {
196+
return err
197+
}
198+
199+
request.Header.Set("Content-Type", "application/json")
200+
request.Header.Set("Accept", "application/json")
201+
request.Header.Set("Authorization", "AccessKey "+c.AccessKey)
202+
request.Header.Set("User-Agent", "MessageBird/ApiClient/"+ClientVersion+" Go/"+runtime.Version())
203+
204+
if c.DebugLog != nil {
205+
if data != nil {
206+
c.DebugLog.Printf("HTTP REQUEST: %s %s %s", method, uri.String(), jsonEncoded)
207+
} else {
208+
c.DebugLog.Printf("HTTP REQUEST: %s %s", method, uri.String())
209+
}
210+
}
211+
212+
response, err := c.HTTPClient.Do(request)
213+
if err != nil {
214+
return err
215+
}
216+
217+
defer response.Body.Close()
218+
219+
responseBody, err := ioutil.ReadAll(response.Body)
220+
if err != nil {
221+
return err
222+
}
223+
224+
if c.DebugLog != nil {
225+
c.DebugLog.Printf("HTTP RESPONSE: %s", string(responseBody))
226+
}
227+
228+
// Status code 500 is a server error and means nothing can be done at this
229+
// point.
230+
if response.StatusCode == 500 {
231+
return ErrUnexpectedResponse
232+
}
233+
// Status codes 200 and 201 are indicative of being able to convert the
234+
// response body to the struct that was specified.
235+
if response.StatusCode == 200 || response.StatusCode == 201 {
236+
if err := json.Unmarshal(responseBody, &v); err != nil {
237+
return fmt.Errorf("could not decode response JSON, %s: %v", string(responseBody), err)
238+
}
239+
return nil
240+
}
241+
242+
// Anything else than a 200/201/500 should be a JSON error.
243+
var errorResponse ErrorResponse
244+
if err := json.Unmarshal(responseBody, &errorResponse); err != nil {
245+
return err
246+
}
247+
248+
return errorResponse
249+
}
250+
153251
// Balance returns the balance information for the account that is associated
154252
// with the access key.
155253
func (c *Client) Balance() (*Balance, error) {
@@ -398,7 +496,6 @@ func (c *Client) Lookup(phoneNumber string, params *LookupParams) (*Lookup, erro
398496
if err == ErrResponse {
399497
return lookup, err
400498
}
401-
402499
return nil, err
403500
}
404501

error.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
package messagebird
22

3+
import "fmt"
4+
35
// Error holds details including error code, human readable description and optional parameter that is related to the error.
46
type Error struct {
57
Code int
68
Description string
79
Parameter string
810
}
11+
12+
// ErrorResponse represents errored API response.
13+
type ErrorResponse struct {
14+
Errors []Error `json:"errors"`
15+
}
16+
17+
// Error implements error interface.
18+
func (r ErrorResponse) Error() string {
19+
eString := "API returned an error: "
20+
for i, e := range r.Errors {
21+
eString = eString + fmt.Sprintf("code: %d, description: %s, parameter: %s", e.Code, e.Description, e.Parameter)
22+
if i < len(r.Errors)-1 {
23+
eString = eString + ", "
24+
}
25+
}
26+
27+
return eString
28+
}

error_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package messagebird
2+
3+
import "testing"
4+
5+
func TestErrorResponseError(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
errors []Error
9+
expect string
10+
}{
11+
{
12+
name: "single error",
13+
errors: []Error{
14+
Error{
15+
Code: 2,
16+
Description: "Request not allowed (incorrect access_key)",
17+
Parameter: "access_key",
18+
},
19+
},
20+
expect: "API returned an error: code: 2, description: Request not allowed (incorrect access_key), parameter: access_key",
21+
},
22+
{
23+
name: "multiple errors",
24+
errors: []Error{
25+
Error{
26+
Code: 2,
27+
Description: "Request not allowed (incorrect access_key)",
28+
Parameter: "access_key",
29+
},
30+
Error{
31+
Code: 2,
32+
Description: "Request not allowed (incorrect access_key)",
33+
Parameter: "access_key",
34+
},
35+
},
36+
expect: "API returned an error: code: 2, description: Request not allowed (incorrect access_key), parameter: access_key, code: 2, description: Request not allowed (incorrect access_key), parameter: access_key",
37+
},
38+
}
39+
40+
for _, tt := range tests {
41+
t.Run(tt.name, func(t *testing.T) {
42+
er := ErrorResponse{tt.errors}
43+
if er.Error() != tt.expect {
44+
t.Errorf("expected error message to be:\n%s\ngot:\n%s", tt.expect, er.Error())
45+
}
46+
})
47+
}
48+
}

hlr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type HLR struct {
1717
Details map[string]interface{}
1818
CreatedDatetime *time.Time
1919
StatusDatetime *time.Time
20-
Errors []Error
20+
Errors []Error // Deprecated: errors now returned at ErrorResponse instance as error.
2121
}
2222

2323
// HLRList represents a list of HLR requests.

hlr_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,23 @@ func TestNewHLR(t *testing.T) {
124124

125125
func TestHLRError(t *testing.T) {
126126
SetServerResponse(http.StatusMethodNotAllowed, accessKeyErrorObject)
127+
_, err := mbClient.HLR("dummy_hlr_id")
127128

128-
hlr, err := mbClient.HLR("dummy_hlr_id")
129-
if err != ErrResponse {
130-
t.Fatalf("Expected ErrResponse to be returned, instead I got %s", err)
129+
errorResponse, ok := err.(ErrorResponse)
130+
if !ok {
131+
t.Fatalf("Expected ErrorResponse to be returned, instead I got %s", err)
131132
}
132133

133-
if len(hlr.Errors) != 1 {
134-
t.Fatalf("Unexpected number of errors: %d, expected: 1", len(hlr.Errors))
134+
if len(errorResponse.Errors) != 1 {
135+
t.Fatalf("Unexpected number of errors: %d, expected: 1", len(errorResponse.Errors))
135136
}
136137

137-
if hlr.Errors[0].Code != 2 {
138-
t.Errorf("Unexpected error code: %d, expected: 2", hlr.Errors[0].Code)
138+
if errorResponse.Errors[0].Code != 2 {
139+
t.Errorf("Unexpected error code: %d, expected: 2", errorResponse.Errors[0].Code)
139140
}
140141

141-
if hlr.Errors[0].Parameter != "access_key" {
142-
t.Errorf("Unexpected error parameter: %s, expected: access_key", hlr.Errors[0].Parameter)
142+
if errorResponse.Errors[0].Parameter != "access_key" {
143+
t.Errorf("Unexpected error parameter: %s, expected: access_key", errorResponse.Errors[0].Parameter)
143144
}
144145
}
145146

main_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@ func startFauxServer() {
4545
},
4646
}
4747

48-
mbClient = &Client{
49-
AccessKey: "test_gshuPaZoeEG6ovbc8M79w0QyM",
50-
HTTPClient: &http.Client{Transport: transport},
51-
}
48+
mbClient = NewV2("test_gshuPaZoeEG6ovbc8M79w0QyM")
49+
mbClient.HTTPClient = &http.Client{Transport: transport}
5250

5351
if testing.Verbose() {
5452
mbClient.DebugLog = log.New(os.Stdout, "DEBUG", log.Lshortfile)

message.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type Message struct {
2828
ScheduledDatetime *time.Time
2929
CreatedDatetime *time.Time
3030
Recipients Recipients
31-
Errors []Error
31+
Errors []Error // Deprecated: errors now returned at ErrorResponse instance as error.
3232
}
3333

3434
// MessageList represents a list of Messages.

message_test.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,6 @@ func assertMessageObject(t *testing.T, message *Message) {
114114
if message.Recipients.Items[0].StatusDatetime == nil || message.Recipients.Items[0].StatusDatetime.Format(time.RFC3339) != "2015-01-05T10:02:59Z" {
115115
t.Errorf("Unexpected datetime status for message recipient: %s, expected: 2015-01-05T10:02:59Z", message.Recipients.Items[0].StatusDatetime.Format(time.RFC3339))
116116
}
117-
118-
if len(message.Errors) != 0 {
119-
t.Errorf("Unexpected number of errors in message: %d, expected: 0", len(message.Errors))
120-
}
121117
}
122118

123119
func TestNewMessage(t *testing.T) {
@@ -133,22 +129,23 @@ func TestNewMessage(t *testing.T) {
133129

134130
func TestNewMessageError(t *testing.T) {
135131
SetServerResponse(http.StatusMethodNotAllowed, accessKeyErrorObject)
132+
_, err := mbClient.NewMessage("TestName", []string{"31612345678"}, "Hello World", nil)
136133

137-
message, err := mbClient.NewMessage("TestName", []string{"31612345678"}, "Hello World", nil)
138-
if err != ErrResponse {
139-
t.Fatalf("Expected ErrResponse to be returned, instead I got %s", err)
134+
errorResponse, ok := err.(ErrorResponse)
135+
if !ok {
136+
t.Fatalf("Expected ErrorResponse to be returned, instead I got %s", err)
140137
}
141138

142-
if len(message.Errors) != 1 {
143-
t.Fatalf("Unexpected number of errors: %d, expected: 1", len(message.Errors))
139+
if len(errorResponse.Errors) != 1 {
140+
t.Fatalf("Unexpected number of errors: %d, expected: 1", len(errorResponse.Errors))
144141
}
145142

146-
if message.Errors[0].Code != 2 {
147-
t.Errorf("Unexpected error code: %d, expected: 2", message.Errors[0].Code)
143+
if errorResponse.Errors[0].Code != 2 {
144+
t.Errorf("Unexpected error code: %d, expected: 2", errorResponse.Errors[0].Code)
148145
}
149146

150-
if message.Errors[0].Parameter != "access_key" {
151-
t.Errorf("Unexpected error parameter: %s, expected: access_key", message.Errors[0].Parameter)
147+
if errorResponse.Errors[0].Parameter != "access_key" {
148+
t.Errorf("Unexpected error parameter: %s, expected: access_key", errorResponse.Errors[0].Parameter)
152149
}
153150
}
154151

0 commit comments

Comments
 (0)