Custom error type instead of builtin
This commit is contained in:
commit
d40fe94ec5
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16', '1.17']
|
||||
go-version: ['1.13', '1.14', '1.15', '1.16', '1.17']
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v2
|
||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/retailcrm/mg-transport-api-client-go
|
||||
|
||||
go 1.11
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.0.0
|
||||
|
1
go.sum
1
go.sum
@ -15,7 +15,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
44
v1/client.go
44
v1/client.go
@ -71,7 +71,7 @@ func (c *MgClient) TransportTemplates() ([]Template, int, error) {
|
||||
}
|
||||
|
||||
if status > http.StatusCreated || status < http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -116,7 +116,7 @@ func (c *MgClient) ActivateTemplate(channelID uint64, request ActivateTemplateRe
|
||||
}
|
||||
|
||||
if status > http.StatusCreated || status < http.StatusOK {
|
||||
return status, c.Error(data)
|
||||
return status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return status, err
|
||||
@ -165,7 +165,7 @@ func (c *MgClient) UpdateTemplate(request Template) (int, error) {
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return status, c.Error(data)
|
||||
return status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return status, err
|
||||
@ -190,7 +190,7 @@ func (c *MgClient) DeactivateTemplate(channelID uint64, templateCode string) (in
|
||||
}
|
||||
|
||||
if status > http.StatusCreated || status < http.StatusOK {
|
||||
return status, c.Error(data)
|
||||
return status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return status, err
|
||||
@ -224,7 +224,7 @@ func (c *MgClient) TransportChannels(request Channels) ([]ChannelListItem, int,
|
||||
}
|
||||
|
||||
if status > http.StatusCreated || status < http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -283,7 +283,7 @@ func (c *MgClient) ActivateTransportChannel(request Channel) (ActivateResponse,
|
||||
}
|
||||
|
||||
if status > http.StatusCreated || status < http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -342,7 +342,7 @@ func (c *MgClient) UpdateTransportChannel(request Channel) (UpdateResponse, int,
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -378,7 +378,7 @@ func (c *MgClient) DeactivateTransportChannel(id uint64) (DeleteResponse, int, e
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -427,7 +427,7 @@ func (c *MgClient) Messages(request SendData) (MessagesResponse, int, error) {
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -471,7 +471,7 @@ func (c *MgClient) UpdateMessages(request EditMessageRequest) (MessagesResponse,
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -510,7 +510,7 @@ func (c *MgClient) MarkMessageRead(request MarkMessageReadRequest) (MarkMessageR
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -541,7 +541,7 @@ func (c *MgClient) AckMessage(request AckMessageRequest) (int, error) {
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return status, c.Error(data)
|
||||
return status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return status, err
|
||||
@ -579,7 +579,7 @@ func (c *MgClient) DeleteMessage(request DeleteData) (*MessagesResponse, int, er
|
||||
return nil, status, err
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
return nil, status, c.Error(data)
|
||||
return nil, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
var previousChatMessage *MessagesResponse
|
||||
@ -618,7 +618,7 @@ func (c *MgClient) GetFile(request string) (FullFileResponse, int, error) {
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -638,7 +638,7 @@ func (c *MgClient) UploadFile(request io.Reader) (UploadFileResponse, int, error
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
@ -659,24 +659,12 @@ func (c *MgClient) UploadFileByURL(request UploadFileByUrlRequest) (UploadFileRe
|
||||
}
|
||||
|
||||
if status != http.StatusOK {
|
||||
return resp, status, c.Error(data)
|
||||
return resp, status, NewAPIClientError(data)
|
||||
}
|
||||
|
||||
return resp, status, err
|
||||
}
|
||||
|
||||
func (c *MgClient) Error(info []byte) error {
|
||||
var data map[string]interface{}
|
||||
|
||||
if err := json.Unmarshal(info, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
values := data["errors"].([]interface{})
|
||||
|
||||
return errors.New(values[0].(string))
|
||||
}
|
||||
|
||||
// MakeTimestamp returns current unix timestamp.
|
||||
func MakeTimestamp() int64 {
|
||||
return time.Now().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||
|
@ -3,6 +3,7 @@ package v1
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -45,61 +46,65 @@ func (t *MGClientTest) Test_TransportChannels() {
|
||||
t.gock().
|
||||
Get(t.transportURL("channels")).
|
||||
Reply(http.StatusOK).
|
||||
JSON([]ChannelListItem{{
|
||||
ID: 1,
|
||||
ExternalID: "external_id",
|
||||
Type: "whatsapp",
|
||||
Name: &chName,
|
||||
Settings: ChannelSettings{
|
||||
Status: Status{
|
||||
Delivered: ChannelFeatureNone,
|
||||
Read: ChannelFeatureSend,
|
||||
},
|
||||
Text: ChannelSettingsText{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
MaxCharsCount: 4096,
|
||||
},
|
||||
Product: Product{
|
||||
Creating: ChannelFeatureReceive,
|
||||
Editing: ChannelFeatureReceive,
|
||||
},
|
||||
Order: Order{
|
||||
Creating: ChannelFeatureReceive,
|
||||
Editing: ChannelFeatureReceive,
|
||||
},
|
||||
File: ChannelSettingsFilesBase{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
Max: 1,
|
||||
},
|
||||
Image: ChannelSettingsFilesBase{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
Max: 1, // nolint:gomnd
|
||||
},
|
||||
Suggestions: ChannelSettingsSuggestions{
|
||||
Text: ChannelFeatureBoth,
|
||||
Phone: ChannelFeatureBoth,
|
||||
Email: ChannelFeatureBoth,
|
||||
},
|
||||
CustomerExternalID: ChannelFeatureCustomerExternalIDPhone,
|
||||
SendingPolicy: SendingPolicy{
|
||||
NewCustomer: ChannelFeatureSendingPolicyTemplate,
|
||||
JSON(
|
||||
[]ChannelListItem{
|
||||
{
|
||||
ID: 1,
|
||||
ExternalID: "external_id",
|
||||
Type: "whatsapp",
|
||||
Name: &chName,
|
||||
Settings: ChannelSettings{
|
||||
Status: Status{
|
||||
Delivered: ChannelFeatureNone,
|
||||
Read: ChannelFeatureSend,
|
||||
},
|
||||
Text: ChannelSettingsText{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
MaxCharsCount: 4096,
|
||||
},
|
||||
Product: Product{
|
||||
Creating: ChannelFeatureReceive,
|
||||
Editing: ChannelFeatureReceive,
|
||||
},
|
||||
Order: Order{
|
||||
Creating: ChannelFeatureReceive,
|
||||
Editing: ChannelFeatureReceive,
|
||||
},
|
||||
File: ChannelSettingsFilesBase{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
Max: 1,
|
||||
},
|
||||
Image: ChannelSettingsFilesBase{
|
||||
Creating: ChannelFeatureBoth,
|
||||
Editing: ChannelFeatureBoth,
|
||||
Quoting: ChannelFeatureBoth,
|
||||
Deleting: ChannelFeatureReceive,
|
||||
Max: 1, // nolint:gomnd
|
||||
},
|
||||
Suggestions: ChannelSettingsSuggestions{
|
||||
Text: ChannelFeatureBoth,
|
||||
Phone: ChannelFeatureBoth,
|
||||
Email: ChannelFeatureBoth,
|
||||
},
|
||||
CustomerExternalID: ChannelFeatureCustomerExternalIDPhone,
|
||||
SendingPolicy: SendingPolicy{
|
||||
NewCustomer: ChannelFeatureSendingPolicyTemplate,
|
||||
},
|
||||
},
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: &createdAt,
|
||||
ActivatedAt: createdAt,
|
||||
DeactivatedAt: nil,
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: &createdAt,
|
||||
ActivatedAt: createdAt,
|
||||
DeactivatedAt: nil,
|
||||
IsActive: true,
|
||||
}})
|
||||
)
|
||||
|
||||
data, status, err := c.TransportChannels(Channels{Active: true})
|
||||
t.Require().NoError(err)
|
||||
@ -147,11 +152,13 @@ func (t *MGClientTest) Test_ActivateTransportChannel() {
|
||||
t.gock().
|
||||
Post(t.transportURL("channels")).
|
||||
Reply(http.StatusCreated).
|
||||
JSON(ActivateResponse{
|
||||
ChannelID: 1,
|
||||
ExternalID: "external_id_1",
|
||||
ActivatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
ActivateResponse{
|
||||
ChannelID: 1,
|
||||
ExternalID: "external_id_1",
|
||||
ActivatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
data, status, err := c.ActivateTransportChannel(ch)
|
||||
t.Require().NoError(err)
|
||||
@ -200,19 +207,23 @@ func (t *MGClientTest) Test_ActivateNewTransportChannel() {
|
||||
t.gock().
|
||||
Post(t.transportURL("channels")).
|
||||
Reply(http.StatusCreated).
|
||||
JSON(ActivateResponse{
|
||||
ChannelID: 1,
|
||||
ExternalID: "external_id_1",
|
||||
ActivatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
ActivateResponse{
|
||||
ChannelID: 1,
|
||||
ExternalID: "external_id_1",
|
||||
ActivatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
t.gock().
|
||||
Delete(t.transportURL("channels/1")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(DeleteResponse{
|
||||
ChannelID: 1,
|
||||
DeactivatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
DeleteResponse{
|
||||
ChannelID: 1,
|
||||
DeactivatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
data, status, err := c.ActivateTransportChannel(ch)
|
||||
t.Require().NoError(err)
|
||||
@ -266,11 +277,13 @@ func (t *MGClientTest) Test_UpdateTransportChannel() {
|
||||
t.gock().
|
||||
Put(t.transportURL("channels/1")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(UpdateResponse{
|
||||
ChannelID: uint64(1),
|
||||
ExternalID: "external_id_1",
|
||||
UpdatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
UpdateResponse{
|
||||
ChannelID: uint64(1),
|
||||
ExternalID: "external_id_1",
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
data, status, err := c.UpdateTransportChannel(ch)
|
||||
t.Require().NoError(err)
|
||||
@ -287,27 +300,31 @@ func (t *MGClientTest) Test_TransportTemplates() {
|
||||
t.gock().
|
||||
Get(t.transportURL("templates")).
|
||||
Reply(http.StatusOK).
|
||||
JSON([]Template{{
|
||||
Code: "tpl_code",
|
||||
ChannelID: 1,
|
||||
Name: "Test Template",
|
||||
Enabled: true,
|
||||
Type: TemplateTypeText,
|
||||
Template: []TemplateItem{
|
||||
JSON(
|
||||
[]Template{
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
Text: "Hello, ",
|
||||
},
|
||||
{
|
||||
Type: TemplateItemTypeVar,
|
||||
VarType: TemplateVarFirstName,
|
||||
},
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
Text: "! We're glad to see you back in our store.",
|
||||
Code: "tpl_code",
|
||||
ChannelID: 1,
|
||||
Name: "Test Template",
|
||||
Enabled: true,
|
||||
Type: TemplateTypeText,
|
||||
Template: []TemplateItem{
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
Text: "Hello, ",
|
||||
},
|
||||
{
|
||||
Type: TemplateItemTypeVar,
|
||||
VarType: TemplateVarFirstName,
|
||||
},
|
||||
{
|
||||
Type: TemplateItemTypeText,
|
||||
Text: "! We're glad to see you back in our store.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}})
|
||||
)
|
||||
|
||||
data, status, err := c.TransportTemplates()
|
||||
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
||||
@ -390,10 +407,12 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
||||
defer gock.Off()
|
||||
|
||||
t.gock().
|
||||
Filter(func(r *http.Request) bool {
|
||||
return r.Method == http.MethodPut &&
|
||||
r.URL.Path == "/api/transport/v1/channels/1/templates/encodable#code"
|
||||
}).
|
||||
Filter(
|
||||
func(r *http.Request) bool {
|
||||
return r.Method == http.MethodPut &&
|
||||
r.URL.Path == "/api/transport/v1/channels/1/templates/encodable#code"
|
||||
},
|
||||
).
|
||||
Reply(http.StatusOK).
|
||||
JSON(map[string]interface{}{})
|
||||
|
||||
@ -440,9 +459,11 @@ func (t *MGClientTest) Test_UpdateTemplateFail() {
|
||||
defer gock.Off()
|
||||
t.gock().
|
||||
Reply(http.StatusBadRequest).
|
||||
JSON(map[string][]string{
|
||||
"errors": {"Some weird error message..."},
|
||||
})
|
||||
JSON(
|
||||
map[string][]string{
|
||||
"errors": {"Some weird error message..."},
|
||||
},
|
||||
)
|
||||
|
||||
status, err := c.UpdateTemplate(tpl)
|
||||
t.Assert().Error(err, fmt.Sprintf("%d %s", status, err))
|
||||
@ -453,10 +474,12 @@ func (t *MGClientTest) Test_DeactivateTemplate() {
|
||||
|
||||
defer gock.Off()
|
||||
t.gock().
|
||||
Filter(func(r *http.Request) bool {
|
||||
return r.Method == http.MethodDelete &&
|
||||
r.URL.Path == t.transportURL("channels/1/templates/test_template#code")
|
||||
}).
|
||||
Filter(
|
||||
func(r *http.Request) bool {
|
||||
return r.Method == http.MethodDelete &&
|
||||
r.URL.Path == t.transportURL("channels/1/templates/test_template#code")
|
||||
},
|
||||
).
|
||||
Reply(http.StatusOK).
|
||||
JSON(map[string]interface{}{})
|
||||
|
||||
@ -487,10 +510,12 @@ func (t *MGClientTest) Test_TextMessages() {
|
||||
t.gock().
|
||||
Post(t.transportURL("messages")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
data, status, err := c.Messages(snd)
|
||||
t.Require().NoError(err)
|
||||
@ -507,26 +532,32 @@ func (t *MGClientTest) Test_ImageMessages() {
|
||||
t.gock().
|
||||
Post(t.transportURL("files/upload_by_url")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(UploadFileResponse{
|
||||
ID: "1",
|
||||
Hash: "1",
|
||||
Type: "image/png",
|
||||
MimeType: "",
|
||||
Size: 1024,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
UploadFileResponse{
|
||||
ID: "1",
|
||||
Hash: "1",
|
||||
Type: "image/png",
|
||||
MimeType: "",
|
||||
Size: 1024,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
t.gock().
|
||||
Post(t.transportURL("messages")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
uploadFileResponse, st, err := c.UploadFileByURL(UploadFileByUrlRequest{
|
||||
Url: "https://via.placeholder.com/1",
|
||||
})
|
||||
uploadFileResponse, st, err := c.UploadFileByURL(
|
||||
UploadFileByUrlRequest{
|
||||
Url: "https://via.placeholder.com/1",
|
||||
},
|
||||
)
|
||||
t.Require().NoError(err)
|
||||
t.Assert().Equal(http.StatusOK, st)
|
||||
t.Assert().Equal("1", uploadFileResponse.ID)
|
||||
@ -569,10 +600,12 @@ func (t *MGClientTest) Test_UpdateMessages() {
|
||||
t.gock().
|
||||
Put(t.transportURL("messages")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
MessagesResponse{
|
||||
MessageID: 1,
|
||||
Time: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
dataU, status, err := c.UpdateMessages(sndU)
|
||||
t.Require().NoError(err)
|
||||
@ -599,28 +632,34 @@ func (t *MGClientTest) Test_MarkMessageReadAndDelete() {
|
||||
|
||||
t.gock().
|
||||
Delete(t.transportURL("messages")).
|
||||
JSON(DeleteData{
|
||||
Message: Message{
|
||||
ExternalID: "deleted",
|
||||
JSON(
|
||||
DeleteData{
|
||||
Message: Message{
|
||||
ExternalID: "deleted",
|
||||
},
|
||||
Channel: 1,
|
||||
},
|
||||
Channel: 1,
|
||||
}).
|
||||
).
|
||||
Reply(http.StatusOK).
|
||||
JSON(MessagesResponse{
|
||||
MessageID: 2,
|
||||
Time: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
MessagesResponse{
|
||||
MessageID: 2,
|
||||
Time: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
_, status, err := c.MarkMessageRead(snd)
|
||||
t.Require().NoError(err)
|
||||
t.Assert().Equal(http.StatusOK, status)
|
||||
|
||||
previousChatMessage, status, err := c.DeleteMessage(DeleteData{
|
||||
Message{
|
||||
ExternalID: "deleted",
|
||||
previousChatMessage, status, err := c.DeleteMessage(
|
||||
DeleteData{
|
||||
Message{
|
||||
ExternalID: "deleted",
|
||||
},
|
||||
1,
|
||||
},
|
||||
1,
|
||||
})
|
||||
)
|
||||
t.Require().NoError(err)
|
||||
t.Assert().Equal(http.StatusOK, status)
|
||||
t.Assert().Equal(2, previousChatMessage.MessageID)
|
||||
@ -633,10 +672,12 @@ func (t *MGClientTest) Test_DeactivateTransportChannel() {
|
||||
t.gock().
|
||||
Delete(t.transportURL("channels/1")).
|
||||
Reply(http.StatusOK).
|
||||
JSON(DeleteResponse{
|
||||
ChannelID: 1,
|
||||
DeactivatedAt: time.Now(),
|
||||
})
|
||||
JSON(
|
||||
DeleteResponse{
|
||||
ChannelID: 1,
|
||||
DeactivatedAt: time.Now(),
|
||||
},
|
||||
)
|
||||
|
||||
deleteData, status, err := c.DeactivateTransportChannel(1)
|
||||
t.Require().NoError(err)
|
||||
@ -678,3 +719,35 @@ func (t *MGClientTest) Test_UploadFile() {
|
||||
resp.CreatedAt = data.CreatedAt
|
||||
t.Assert().Equal(resp, data)
|
||||
}
|
||||
|
||||
func (t *MGClientTest) Test_SuccessHandleError() {
|
||||
client := t.client()
|
||||
json := `{"errors": ["Channel not found"]}`
|
||||
|
||||
defer gock.Off()
|
||||
t.gock().
|
||||
Delete(t.transportURL("channels/123")).
|
||||
Reply(http.StatusInternalServerError)
|
||||
|
||||
t.gock().
|
||||
Delete(t.transportURL("channels/455")).
|
||||
Reply(http.StatusBadRequest).
|
||||
JSON(json)
|
||||
|
||||
_, statusCode, err := client.DeactivateTransportChannel(123)
|
||||
|
||||
t.Assert().Equal(http.StatusInternalServerError, statusCode)
|
||||
t.Assert().IsType(new(HTTPClientError), err)
|
||||
t.Assert().Equal(internalServerError, err.Error())
|
||||
var serverErr *HTTPClientError
|
||||
if errors.As(err, &serverErr) {
|
||||
t.Assert().Nil(serverErr.Response)
|
||||
} else {
|
||||
t.Fail("Unexpected type of error")
|
||||
}
|
||||
|
||||
_, statusCode, err = client.DeactivateTransportChannel(455)
|
||||
t.Assert().Equal(http.StatusBadRequest, statusCode)
|
||||
t.Assert().IsType(new(HTTPClientError), err)
|
||||
t.Assert().Equal("Channel not found", err.Error())
|
||||
}
|
||||
|
77
v1/errors.go
Normal file
77
v1/errors.go
Normal file
@ -0,0 +1,77 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var defaultErrorMessage = "http client error"
|
||||
var internalServerError = "internal server error"
|
||||
var marshalError = "cannot unmarshal response body"
|
||||
|
||||
type MGErrors struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
type HTTPClientError struct {
|
||||
ErrorMsg string
|
||||
BaseError error
|
||||
Response io.Reader
|
||||
}
|
||||
|
||||
func (err *HTTPClientError) Unwrap() error {
|
||||
return err.BaseError
|
||||
}
|
||||
|
||||
func (err *HTTPClientError) Error() string {
|
||||
message := defaultErrorMessage
|
||||
|
||||
if err.BaseError != nil {
|
||||
message = fmt.Sprintf("%s: %s", defaultErrorMessage, err.BaseError.Error())
|
||||
} else if len(err.ErrorMsg) > 0 {
|
||||
message = err.ErrorMsg
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func NewCriticalHTTPError(err error) error {
|
||||
return &HTTPClientError{BaseError: err}
|
||||
}
|
||||
|
||||
func NewAPIClientError(responseBody []byte) error {
|
||||
var data MGErrors
|
||||
var message string
|
||||
|
||||
if len(responseBody) == 0 {
|
||||
message = internalServerError
|
||||
} else {
|
||||
err := json.Unmarshal(responseBody, &data)
|
||||
|
||||
if err != nil {
|
||||
message = marshalError
|
||||
} else if len(data.Errors) > 0 {
|
||||
message = data.Errors[0]
|
||||
}
|
||||
}
|
||||
|
||||
return &HTTPClientError{ErrorMsg: message}
|
||||
}
|
||||
|
||||
func NewServerError(response *http.Response) error {
|
||||
var serverError *HTTPClientError
|
||||
|
||||
body, _ := buildLimitedRawResponse(response)
|
||||
err := NewAPIClientError(body)
|
||||
|
||||
if errors.As(err, &serverError) && len(body) > 0 {
|
||||
serverError.Response = bytes.NewBuffer(body)
|
||||
return serverError
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
66
v1/errors_test.go
Normal file
66
v1/errors_test.go
Normal file
@ -0,0 +1,66 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewCriticalHTTPError(t *testing.T) {
|
||||
err := &url.Error{Op: "Get", URL: "http//example.com", Err: errors.New("EOF")}
|
||||
httpErr := NewCriticalHTTPError(err)
|
||||
|
||||
assert.IsType(t, new(HTTPClientError), httpErr)
|
||||
assert.IsType(t, new(url.Error), errors.Unwrap(httpErr))
|
||||
assert.IsType(t, new(url.Error), errors.Unwrap(httpErr))
|
||||
assert.Equal(t, httpErr.Error(), fmt.Sprintf("%s: %s", defaultErrorMessage, err.Error()))
|
||||
}
|
||||
|
||||
func TestNewApiClientError(t *testing.T) {
|
||||
body := []byte(`{"errors" : ["Channel not found"]}`)
|
||||
httpErr := NewAPIClientError(body)
|
||||
|
||||
assert.IsType(t, new(HTTPClientError), httpErr)
|
||||
assert.Equal(t, httpErr.Error(), "Channel not found")
|
||||
|
||||
body = []byte{}
|
||||
httpErr = NewAPIClientError(body)
|
||||
|
||||
assert.IsType(t, new(HTTPClientError), httpErr)
|
||||
assert.Equal(t, httpErr.Error(), internalServerError)
|
||||
}
|
||||
|
||||
func TestNewServerError(t *testing.T) {
|
||||
body := []byte(`{"errors" : ["Something went wrong"]}`)
|
||||
response := new(http.Response)
|
||||
response.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
serverErr := NewServerError(response)
|
||||
|
||||
assert.IsType(t, new(HTTPClientError), serverErr)
|
||||
assert.Equal(t, serverErr.Error(), "Something went wrong")
|
||||
|
||||
var err *HTTPClientError
|
||||
if errors.As(serverErr, &err) {
|
||||
assert.NotNil(t, err.Response)
|
||||
} else {
|
||||
t.Fatal("Unexpected type of error")
|
||||
}
|
||||
|
||||
body = []byte(`{"invalid_json"`)
|
||||
response = new(http.Response)
|
||||
response.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
serverErr = NewServerError(response)
|
||||
|
||||
assert.IsType(t, new(HTTPClientError), serverErr)
|
||||
assert.Equal(t, serverErr.Error(), marshalError)
|
||||
|
||||
if errors.As(serverErr, &err) {
|
||||
assert.NotNil(t, err.Response)
|
||||
}
|
||||
}
|
22
v1/helpers.go
Normal file
22
v1/helpers.go
Normal file
@ -0,0 +1,22 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const MB = 1 << 20
|
||||
|
||||
func buildLimitedRawResponse(resp *http.Response) ([]byte, error) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
limitReader := io.LimitReader(resp.Body, MB)
|
||||
body, err := ioutil.ReadAll(limitReader)
|
||||
|
||||
if err != nil {
|
||||
return body, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
@ -71,15 +70,15 @@ func makeRequest(reqType, url string, buf io.Reader, c *MgClient) ([]byte, int,
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return res, 0, err
|
||||
return res, 0, NewCriticalHTTPError(err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusInternalServerError {
|
||||
err = fmt.Errorf("http request error. status code: %d", resp.StatusCode)
|
||||
err = NewServerError(resp)
|
||||
return res, resp.StatusCode, err
|
||||
}
|
||||
|
||||
res, err = buildRawResponse(resp)
|
||||
res, err = buildLimitedRawResponse(resp)
|
||||
if err != nil {
|
||||
return res, 0, err
|
||||
}
|
||||
@ -90,14 +89,3 @@ func makeRequest(reqType, url string, buf io.Reader, c *MgClient) ([]byte, int,
|
||||
|
||||
return res, resp.StatusCode, err
|
||||
}
|
||||
|
||||
func buildRawResponse(resp *http.Response) ([]byte, error) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
res, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user