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
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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:
|
steps:
|
||||||
- name: Set up Go ${{ matrix.go-version }}
|
- name: Set up Go ${{ matrix.go-version }}
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/retailcrm/mg-transport-api-client-go
|
module github.com/retailcrm/mg-transport-api-client-go
|
||||||
|
|
||||||
go 1.11
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/go-querystring v1.0.0
|
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/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 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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 {
|
if status > http.StatusCreated || status < http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -116,7 +116,7 @@ func (c *MgClient) ActivateTemplate(channelID uint64, request ActivateTemplateRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status > http.StatusCreated || status < http.StatusOK {
|
if status > http.StatusCreated || status < http.StatusOK {
|
||||||
return status, c.Error(data)
|
return status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, err
|
return status, err
|
||||||
@ -165,7 +165,7 @@ func (c *MgClient) UpdateTemplate(request Template) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return status, c.Error(data)
|
return status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, err
|
return status, err
|
||||||
@ -190,7 +190,7 @@ func (c *MgClient) DeactivateTemplate(channelID uint64, templateCode string) (in
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status > http.StatusCreated || status < http.StatusOK {
|
if status > http.StatusCreated || status < http.StatusOK {
|
||||||
return status, c.Error(data)
|
return status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, err
|
return status, err
|
||||||
@ -224,7 +224,7 @@ func (c *MgClient) TransportChannels(request Channels) ([]ChannelListItem, int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status > http.StatusCreated || status < http.StatusOK {
|
if status > http.StatusCreated || status < http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -283,7 +283,7 @@ func (c *MgClient) ActivateTransportChannel(request Channel) (ActivateResponse,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status > http.StatusCreated || status < http.StatusOK {
|
if status > http.StatusCreated || status < http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -342,7 +342,7 @@ func (c *MgClient) UpdateTransportChannel(request Channel) (UpdateResponse, int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -378,7 +378,7 @@ func (c *MgClient) DeactivateTransportChannel(id uint64) (DeleteResponse, int, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -427,7 +427,7 @@ func (c *MgClient) Messages(request SendData) (MessagesResponse, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -471,7 +471,7 @@ func (c *MgClient) UpdateMessages(request EditMessageRequest) (MessagesResponse,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -510,7 +510,7 @@ func (c *MgClient) MarkMessageRead(request MarkMessageReadRequest) (MarkMessageR
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -541,7 +541,7 @@ func (c *MgClient) AckMessage(request AckMessageRequest) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return status, c.Error(data)
|
return status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, err
|
return status, err
|
||||||
@ -579,7 +579,7 @@ func (c *MgClient) DeleteMessage(request DeleteData) (*MessagesResponse, int, er
|
|||||||
return nil, status, err
|
return nil, status, err
|
||||||
}
|
}
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return nil, status, c.Error(data)
|
return nil, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousChatMessage *MessagesResponse
|
var previousChatMessage *MessagesResponse
|
||||||
@ -618,7 +618,7 @@ func (c *MgClient) GetFile(request string) (FullFileResponse, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -638,7 +638,7 @@ func (c *MgClient) UploadFile(request io.Reader) (UploadFileResponse, int, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
return resp, status, err
|
||||||
@ -659,24 +659,12 @@ func (c *MgClient) UploadFileByURL(request UploadFileByUrlRequest) (UploadFileRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if status != http.StatusOK {
|
if status != http.StatusOK {
|
||||||
return resp, status, c.Error(data)
|
return resp, status, NewAPIClientError(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, status, err
|
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.
|
// MakeTimestamp returns current unix timestamp.
|
||||||
func MakeTimestamp() int64 {
|
func MakeTimestamp() int64 {
|
||||||
return time.Now().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
return time.Now().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||||
|
@ -3,6 +3,7 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -45,61 +46,65 @@ func (t *MGClientTest) Test_TransportChannels() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Get(t.transportURL("channels")).
|
Get(t.transportURL("channels")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON([]ChannelListItem{{
|
JSON(
|
||||||
ID: 1,
|
[]ChannelListItem{
|
||||||
ExternalID: "external_id",
|
{
|
||||||
Type: "whatsapp",
|
ID: 1,
|
||||||
Name: &chName,
|
ExternalID: "external_id",
|
||||||
Settings: ChannelSettings{
|
Type: "whatsapp",
|
||||||
Status: Status{
|
Name: &chName,
|
||||||
Delivered: ChannelFeatureNone,
|
Settings: ChannelSettings{
|
||||||
Read: ChannelFeatureSend,
|
Status: Status{
|
||||||
},
|
Delivered: ChannelFeatureNone,
|
||||||
Text: ChannelSettingsText{
|
Read: ChannelFeatureSend,
|
||||||
Creating: ChannelFeatureBoth,
|
},
|
||||||
Editing: ChannelFeatureBoth,
|
Text: ChannelSettingsText{
|
||||||
Quoting: ChannelFeatureBoth,
|
Creating: ChannelFeatureBoth,
|
||||||
Deleting: ChannelFeatureReceive,
|
Editing: ChannelFeatureBoth,
|
||||||
MaxCharsCount: 4096,
|
Quoting: ChannelFeatureBoth,
|
||||||
},
|
Deleting: ChannelFeatureReceive,
|
||||||
Product: Product{
|
MaxCharsCount: 4096,
|
||||||
Creating: ChannelFeatureReceive,
|
},
|
||||||
Editing: ChannelFeatureReceive,
|
Product: Product{
|
||||||
},
|
Creating: ChannelFeatureReceive,
|
||||||
Order: Order{
|
Editing: ChannelFeatureReceive,
|
||||||
Creating: ChannelFeatureReceive,
|
},
|
||||||
Editing: ChannelFeatureReceive,
|
Order: Order{
|
||||||
},
|
Creating: ChannelFeatureReceive,
|
||||||
File: ChannelSettingsFilesBase{
|
Editing: ChannelFeatureReceive,
|
||||||
Creating: ChannelFeatureBoth,
|
},
|
||||||
Editing: ChannelFeatureBoth,
|
File: ChannelSettingsFilesBase{
|
||||||
Quoting: ChannelFeatureBoth,
|
Creating: ChannelFeatureBoth,
|
||||||
Deleting: ChannelFeatureReceive,
|
Editing: ChannelFeatureBoth,
|
||||||
Max: 1,
|
Quoting: ChannelFeatureBoth,
|
||||||
},
|
Deleting: ChannelFeatureReceive,
|
||||||
Image: ChannelSettingsFilesBase{
|
Max: 1,
|
||||||
Creating: ChannelFeatureBoth,
|
},
|
||||||
Editing: ChannelFeatureBoth,
|
Image: ChannelSettingsFilesBase{
|
||||||
Quoting: ChannelFeatureBoth,
|
Creating: ChannelFeatureBoth,
|
||||||
Deleting: ChannelFeatureReceive,
|
Editing: ChannelFeatureBoth,
|
||||||
Max: 1, // nolint:gomnd
|
Quoting: ChannelFeatureBoth,
|
||||||
},
|
Deleting: ChannelFeatureReceive,
|
||||||
Suggestions: ChannelSettingsSuggestions{
|
Max: 1, // nolint:gomnd
|
||||||
Text: ChannelFeatureBoth,
|
},
|
||||||
Phone: ChannelFeatureBoth,
|
Suggestions: ChannelSettingsSuggestions{
|
||||||
Email: ChannelFeatureBoth,
|
Text: ChannelFeatureBoth,
|
||||||
},
|
Phone: ChannelFeatureBoth,
|
||||||
CustomerExternalID: ChannelFeatureCustomerExternalIDPhone,
|
Email: ChannelFeatureBoth,
|
||||||
SendingPolicy: SendingPolicy{
|
},
|
||||||
NewCustomer: ChannelFeatureSendingPolicyTemplate,
|
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})
|
data, status, err := c.TransportChannels(Channels{Active: true})
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -147,11 +152,13 @@ func (t *MGClientTest) Test_ActivateTransportChannel() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Post(t.transportURL("channels")).
|
Post(t.transportURL("channels")).
|
||||||
Reply(http.StatusCreated).
|
Reply(http.StatusCreated).
|
||||||
JSON(ActivateResponse{
|
JSON(
|
||||||
ChannelID: 1,
|
ActivateResponse{
|
||||||
ExternalID: "external_id_1",
|
ChannelID: 1,
|
||||||
ActivatedAt: time.Now(),
|
ExternalID: "external_id_1",
|
||||||
})
|
ActivatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
data, status, err := c.ActivateTransportChannel(ch)
|
data, status, err := c.ActivateTransportChannel(ch)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -200,19 +207,23 @@ func (t *MGClientTest) Test_ActivateNewTransportChannel() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Post(t.transportURL("channels")).
|
Post(t.transportURL("channels")).
|
||||||
Reply(http.StatusCreated).
|
Reply(http.StatusCreated).
|
||||||
JSON(ActivateResponse{
|
JSON(
|
||||||
ChannelID: 1,
|
ActivateResponse{
|
||||||
ExternalID: "external_id_1",
|
ChannelID: 1,
|
||||||
ActivatedAt: time.Now(),
|
ExternalID: "external_id_1",
|
||||||
})
|
ActivatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
t.gock().
|
t.gock().
|
||||||
Delete(t.transportURL("channels/1")).
|
Delete(t.transportURL("channels/1")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(DeleteResponse{
|
JSON(
|
||||||
ChannelID: 1,
|
DeleteResponse{
|
||||||
DeactivatedAt: time.Now(),
|
ChannelID: 1,
|
||||||
})
|
DeactivatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
data, status, err := c.ActivateTransportChannel(ch)
|
data, status, err := c.ActivateTransportChannel(ch)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -266,11 +277,13 @@ func (t *MGClientTest) Test_UpdateTransportChannel() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Put(t.transportURL("channels/1")).
|
Put(t.transportURL("channels/1")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(UpdateResponse{
|
JSON(
|
||||||
ChannelID: uint64(1),
|
UpdateResponse{
|
||||||
ExternalID: "external_id_1",
|
ChannelID: uint64(1),
|
||||||
UpdatedAt: time.Now(),
|
ExternalID: "external_id_1",
|
||||||
})
|
UpdatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
data, status, err := c.UpdateTransportChannel(ch)
|
data, status, err := c.UpdateTransportChannel(ch)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -287,27 +300,31 @@ func (t *MGClientTest) Test_TransportTemplates() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Get(t.transportURL("templates")).
|
Get(t.transportURL("templates")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON([]Template{{
|
JSON(
|
||||||
Code: "tpl_code",
|
[]Template{
|
||||||
ChannelID: 1,
|
|
||||||
Name: "Test Template",
|
|
||||||
Enabled: true,
|
|
||||||
Type: TemplateTypeText,
|
|
||||||
Template: []TemplateItem{
|
|
||||||
{
|
{
|
||||||
Type: TemplateItemTypeText,
|
Code: "tpl_code",
|
||||||
Text: "Hello, ",
|
ChannelID: 1,
|
||||||
},
|
Name: "Test Template",
|
||||||
{
|
Enabled: true,
|
||||||
Type: TemplateItemTypeVar,
|
Type: TemplateTypeText,
|
||||||
VarType: TemplateVarFirstName,
|
Template: []TemplateItem{
|
||||||
},
|
{
|
||||||
{
|
Type: TemplateItemTypeText,
|
||||||
Type: TemplateItemTypeText,
|
Text: "Hello, ",
|
||||||
Text: "! We're glad to see you back in our store.",
|
},
|
||||||
|
{
|
||||||
|
Type: TemplateItemTypeVar,
|
||||||
|
VarType: TemplateVarFirstName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: TemplateItemTypeText,
|
||||||
|
Text: "! We're glad to see you back in our store.",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}})
|
)
|
||||||
|
|
||||||
data, status, err := c.TransportTemplates()
|
data, status, err := c.TransportTemplates()
|
||||||
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
t.Assert().NoError(err, fmt.Sprintf("%d %s", status, err))
|
||||||
@ -390,10 +407,12 @@ func (t *MGClientTest) Test_UpdateTemplate() {
|
|||||||
defer gock.Off()
|
defer gock.Off()
|
||||||
|
|
||||||
t.gock().
|
t.gock().
|
||||||
Filter(func(r *http.Request) bool {
|
Filter(
|
||||||
return r.Method == http.MethodPut &&
|
func(r *http.Request) bool {
|
||||||
r.URL.Path == "/api/transport/v1/channels/1/templates/encodable#code"
|
return r.Method == http.MethodPut &&
|
||||||
}).
|
r.URL.Path == "/api/transport/v1/channels/1/templates/encodable#code"
|
||||||
|
},
|
||||||
|
).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(map[string]interface{}{})
|
JSON(map[string]interface{}{})
|
||||||
|
|
||||||
@ -440,9 +459,11 @@ func (t *MGClientTest) Test_UpdateTemplateFail() {
|
|||||||
defer gock.Off()
|
defer gock.Off()
|
||||||
t.gock().
|
t.gock().
|
||||||
Reply(http.StatusBadRequest).
|
Reply(http.StatusBadRequest).
|
||||||
JSON(map[string][]string{
|
JSON(
|
||||||
"errors": {"Some weird error message..."},
|
map[string][]string{
|
||||||
})
|
"errors": {"Some weird error message..."},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
status, err := c.UpdateTemplate(tpl)
|
status, err := c.UpdateTemplate(tpl)
|
||||||
t.Assert().Error(err, fmt.Sprintf("%d %s", status, err))
|
t.Assert().Error(err, fmt.Sprintf("%d %s", status, err))
|
||||||
@ -453,10 +474,12 @@ func (t *MGClientTest) Test_DeactivateTemplate() {
|
|||||||
|
|
||||||
defer gock.Off()
|
defer gock.Off()
|
||||||
t.gock().
|
t.gock().
|
||||||
Filter(func(r *http.Request) bool {
|
Filter(
|
||||||
return r.Method == http.MethodDelete &&
|
func(r *http.Request) bool {
|
||||||
r.URL.Path == t.transportURL("channels/1/templates/test_template#code")
|
return r.Method == http.MethodDelete &&
|
||||||
}).
|
r.URL.Path == t.transportURL("channels/1/templates/test_template#code")
|
||||||
|
},
|
||||||
|
).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(map[string]interface{}{})
|
JSON(map[string]interface{}{})
|
||||||
|
|
||||||
@ -487,10 +510,12 @@ func (t *MGClientTest) Test_TextMessages() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Post(t.transportURL("messages")).
|
Post(t.transportURL("messages")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(MessagesResponse{
|
JSON(
|
||||||
MessageID: 1,
|
MessagesResponse{
|
||||||
Time: time.Now(),
|
MessageID: 1,
|
||||||
})
|
Time: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
data, status, err := c.Messages(snd)
|
data, status, err := c.Messages(snd)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -507,26 +532,32 @@ func (t *MGClientTest) Test_ImageMessages() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Post(t.transportURL("files/upload_by_url")).
|
Post(t.transportURL("files/upload_by_url")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(UploadFileResponse{
|
JSON(
|
||||||
ID: "1",
|
UploadFileResponse{
|
||||||
Hash: "1",
|
ID: "1",
|
||||||
Type: "image/png",
|
Hash: "1",
|
||||||
MimeType: "",
|
Type: "image/png",
|
||||||
Size: 1024,
|
MimeType: "",
|
||||||
CreatedAt: time.Now(),
|
Size: 1024,
|
||||||
})
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
t.gock().
|
t.gock().
|
||||||
Post(t.transportURL("messages")).
|
Post(t.transportURL("messages")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(MessagesResponse{
|
JSON(
|
||||||
MessageID: 1,
|
MessagesResponse{
|
||||||
Time: time.Now(),
|
MessageID: 1,
|
||||||
})
|
Time: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
uploadFileResponse, st, err := c.UploadFileByURL(UploadFileByUrlRequest{
|
uploadFileResponse, st, err := c.UploadFileByURL(
|
||||||
Url: "https://via.placeholder.com/1",
|
UploadFileByUrlRequest{
|
||||||
})
|
Url: "https://via.placeholder.com/1",
|
||||||
|
},
|
||||||
|
)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
t.Assert().Equal(http.StatusOK, st)
|
t.Assert().Equal(http.StatusOK, st)
|
||||||
t.Assert().Equal("1", uploadFileResponse.ID)
|
t.Assert().Equal("1", uploadFileResponse.ID)
|
||||||
@ -569,10 +600,12 @@ func (t *MGClientTest) Test_UpdateMessages() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Put(t.transportURL("messages")).
|
Put(t.transportURL("messages")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(MessagesResponse{
|
JSON(
|
||||||
MessageID: 1,
|
MessagesResponse{
|
||||||
Time: time.Now(),
|
MessageID: 1,
|
||||||
})
|
Time: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
dataU, status, err := c.UpdateMessages(sndU)
|
dataU, status, err := c.UpdateMessages(sndU)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -599,28 +632,34 @@ func (t *MGClientTest) Test_MarkMessageReadAndDelete() {
|
|||||||
|
|
||||||
t.gock().
|
t.gock().
|
||||||
Delete(t.transportURL("messages")).
|
Delete(t.transportURL("messages")).
|
||||||
JSON(DeleteData{
|
JSON(
|
||||||
Message: Message{
|
DeleteData{
|
||||||
ExternalID: "deleted",
|
Message: Message{
|
||||||
|
ExternalID: "deleted",
|
||||||
|
},
|
||||||
|
Channel: 1,
|
||||||
},
|
},
|
||||||
Channel: 1,
|
).
|
||||||
}).
|
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(MessagesResponse{
|
JSON(
|
||||||
MessageID: 2,
|
MessagesResponse{
|
||||||
Time: time.Now(),
|
MessageID: 2,
|
||||||
})
|
Time: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
_, status, err := c.MarkMessageRead(snd)
|
_, status, err := c.MarkMessageRead(snd)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
t.Assert().Equal(http.StatusOK, status)
|
t.Assert().Equal(http.StatusOK, status)
|
||||||
|
|
||||||
previousChatMessage, status, err := c.DeleteMessage(DeleteData{
|
previousChatMessage, status, err := c.DeleteMessage(
|
||||||
Message{
|
DeleteData{
|
||||||
ExternalID: "deleted",
|
Message{
|
||||||
|
ExternalID: "deleted",
|
||||||
|
},
|
||||||
|
1,
|
||||||
},
|
},
|
||||||
1,
|
)
|
||||||
})
|
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
t.Assert().Equal(http.StatusOK, status)
|
t.Assert().Equal(http.StatusOK, status)
|
||||||
t.Assert().Equal(2, previousChatMessage.MessageID)
|
t.Assert().Equal(2, previousChatMessage.MessageID)
|
||||||
@ -633,10 +672,12 @@ func (t *MGClientTest) Test_DeactivateTransportChannel() {
|
|||||||
t.gock().
|
t.gock().
|
||||||
Delete(t.transportURL("channels/1")).
|
Delete(t.transportURL("channels/1")).
|
||||||
Reply(http.StatusOK).
|
Reply(http.StatusOK).
|
||||||
JSON(DeleteResponse{
|
JSON(
|
||||||
ChannelID: 1,
|
DeleteResponse{
|
||||||
DeactivatedAt: time.Now(),
|
ChannelID: 1,
|
||||||
})
|
DeactivatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
deleteData, status, err := c.DeactivateTransportChannel(1)
|
deleteData, status, err := c.DeactivateTransportChannel(1)
|
||||||
t.Require().NoError(err)
|
t.Require().NoError(err)
|
||||||
@ -678,3 +719,35 @@ func (t *MGClientTest) Test_UploadFile() {
|
|||||||
resp.CreatedAt = data.CreatedAt
|
resp.CreatedAt = data.CreatedAt
|
||||||
t.Assert().Equal(resp, data)
|
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"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -71,15 +70,15 @@ func makeRequest(reqType, url string, buf io.Reader, c *MgClient) ([]byte, int,
|
|||||||
|
|
||||||
resp, err := c.httpClient.Do(req)
|
resp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, 0, err
|
return res, 0, NewCriticalHTTPError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode >= http.StatusInternalServerError {
|
if resp.StatusCode >= http.StatusInternalServerError {
|
||||||
err = fmt.Errorf("http request error. status code: %d", resp.StatusCode)
|
err = NewServerError(resp)
|
||||||
return res, resp.StatusCode, err
|
return res, resp.StatusCode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = buildRawResponse(resp)
|
res, err = buildLimitedRawResponse(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, 0, err
|
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
|
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