1
0
mirror of synced 2024-11-21 20:46:05 +03:00

refactoring

This commit is contained in:
Ruslan Efanov 2022-10-26 10:54:54 +03:00
parent fa11f4ffd3
commit bf7e5cdb93
7 changed files with 275 additions and 186 deletions

1
go.sum
View File

@ -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=

View File

@ -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)
@ -679,9 +720,10 @@ func (t *MGClientTest) Test_UploadFile() {
t.Assert().Equal(resp, data)
}
func (t *MGClientTest) Test_SuccessHandleAPIError() {
func (t *MGClientTest) Test_SuccessHandleError() {
client := t.client()
handleError := client.Error([]byte(`{"errors": ["Channel not found"]}`))
json := `{"errors": ["Channel not found"]}`
handleError := client.Error([]byte(json))
t.Assert().IsType(new(httpClientError), handleError)
t.Assert().Equal(handleError.Error(), "Channel not found")
@ -691,9 +733,25 @@ func (t *MGClientTest) Test_SuccessHandleAPIError() {
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("Internal server error", err.Error())
var serverErr *httpClientError
if errors.As(err, &serverErr) {
t.Assert().NotNil(serverErr.ResponseBody)
} 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())
}

View File

@ -2,14 +2,19 @@ package v1
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
var defaultErrorMessage = "Internal http client error"
var internalServerError = "Internal server error"
type httpClientError struct {
ErrorMsg string
BaseError error
ErrorMsg string
BaseError error
ResponseBody io.ReadCloser
}
func (err *httpClientError) Unwrap() error {
@ -20,10 +25,10 @@ func (err *httpClientError) Error() string {
message := defaultErrorMessage
if err.BaseError != nil {
message = err.BaseError.Error()
message = fmt.Sprintf("%s - %s", defaultErrorMessage, err.BaseError.Error())
}
if len([]rune(err.ErrorMsg)) > 0 {
if len(err.ErrorMsg) > 0 {
message = err.ErrorMsg
}
@ -50,4 +55,22 @@ func NewAPIClientError(responseBody []byte) error {
}
return &httpClientError{ErrorMsg: message}
}
}
func NewServerError(response *http.Response) error {
var data []byte
body, err := buildRawResponse(response)
if err == nil {
data = body
}
err = NewAPIClientError(data)
var serverError *httpClientError
if errors.As(err, &serverError) {
serverError.ResponseBody = response.Body
return serverError
}
return err
}

View File

@ -1,7 +1,11 @@
package v1
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"testing"
@ -15,7 +19,7 @@ func TestNewCriticalHTTPError(t *testing.T) {
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(), err.Error())
assert.Equal(t, httpErr.Error(), fmt.Sprintf("%s - %s", defaultErrorMessage, err.Error()))
}
func TestNewApiClientError(t *testing.T) {
@ -30,4 +34,21 @@ func TestNewApiClientError(t *testing.T) {
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 = io.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.ResponseBody)
} else {
t.Fatal("Unexpected type of error")
}
}

17
v1/helpers.go Normal file
View File

@ -0,0 +1,17 @@
package v1
import (
"io"
"net/http"
)
func buildRawResponse(resp *http.Response) ([]byte, error) {
defer resp.Body.Close()
res, err := io.ReadAll(resp.Body)
if err != nil {
return res, err
}
return res, nil
}

View File

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
)
@ -74,30 +73,19 @@ func makeRequest(reqType, url string, buf io.Reader, c *MgClient) ([]byte, int,
return res, 0, NewCriticalHTTPError(err)
}
if resp.StatusCode >= http.StatusInternalServerError {
err = NewServerError(resp)
return res, resp.StatusCode, err
}
res, err = buildRawResponse(resp)
if err != nil {
return res, 0, err
}
if resp.StatusCode >= http.StatusInternalServerError {
err = NewAPIClientError(res)
return res, resp.StatusCode, err
}
if c.Debug {
c.writeLog("MG TRANSPORT API Response: %s", res)
}
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
}

View File

@ -66,23 +66,6 @@ const (
OriginatorChannel
)
// ClientError is error of http-client or network.
type ClientError string
func (err ClientError) Error() string {
return string(err)
}
// APIError is error with 5xx status code.
type APIError struct {
code int
errorMsg string
}
func (err APIError) Error() string {
return fmt.Sprintf("Error message: %s, code: %v", err.errorMsg, err.code)
}
type ErrorType string
const (