mirror of
https://github.com/retailcrm/api-client-go.git
synced 2025-04-04 05:33:32 +03:00
refactor library and upgrade version
This commit is contained in:
parent
269764175e
commit
acb7963d15
18 changed files with 235 additions and 102 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@
|
|||
|
||||
# Folders
|
||||
_obj
|
||||
vendor
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
|
|
|
@ -146,7 +146,7 @@ linters-settings:
|
|||
gocyclo:
|
||||
min-complexity: 25
|
||||
goimports:
|
||||
local-prefixes: github.com/retailcrm/api-client-go
|
||||
local-prefixes: github.com/retailcrm/api-client-go/v2
|
||||
lll:
|
||||
line-length: 120
|
||||
maligned:
|
||||
|
|
|
@ -13,19 +13,19 @@ This is golang RetailCRM API client.
|
|||
## Install
|
||||
|
||||
```bash
|
||||
go get -x github.com/retailcrm/api-client-go
|
||||
go get -u github.com/retailcrm/api-client-go/v2
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```golang
|
||||
package main
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/retailcrm/api-client-go/v5"
|
||||
"github.com/retailcrm/api-client-go/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -26,11 +26,26 @@ func New(url string, key string) *Client {
|
|||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the provided logger instance into the Client.
|
||||
func (c *Client) WithLogger(logger BasicLogger) *Client {
|
||||
c.logger = logger
|
||||
return c
|
||||
}
|
||||
|
||||
// writeLog writes to the log.
|
||||
func (c *Client) writeLog(format string, v ...interface{}) {
|
||||
if c.logger != nil {
|
||||
c.logger.Printf(format, v...)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
// GetRequest implements GET Request.
|
||||
func (c *Client) GetRequest(urlWithParameters string, versioned ...bool) ([]byte, int, error) {
|
||||
var res []byte
|
||||
var prefix = "/api/v5"
|
||||
apiError := &APIError{}
|
||||
|
||||
if len(versioned) > 0 {
|
||||
s := versioned[0]
|
||||
|
@ -48,7 +63,7 @@ func (c *Client) GetRequest(urlWithParameters string, versioned ...bool) ([]byte
|
|||
req.Header.Set("X-API-KEY", c.Key)
|
||||
|
||||
if c.Debug {
|
||||
log.Printf("API Request: %s %s", fmt.Sprintf("%s%s%s", c.URL, prefix, urlWithParameters), c.Key)
|
||||
c.writeLog("API Request: %s %s", fmt.Sprintf("%s%s%s", c.URL, prefix, urlWithParameters), c.Key)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
|
@ -57,8 +72,8 @@ func (c *Client) GetRequest(urlWithParameters string, versioned ...bool) ([]byte
|
|||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusInternalServerError {
|
||||
apiError.ErrorMsg = fmt.Sprintf("HTTP request error. Status code: %d.\n", resp.StatusCode)
|
||||
return res, resp.StatusCode, apiError
|
||||
return res, resp.StatusCode, CreateGenericAPIError(
|
||||
fmt.Sprintf("HTTP request error. Status code: %d.", resp.StatusCode))
|
||||
}
|
||||
|
||||
res, err = buildRawResponse(resp)
|
||||
|
@ -67,11 +82,11 @@ func (c *Client) GetRequest(urlWithParameters string, versioned ...bool) ([]byte
|
|||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest && resp.StatusCode < http.StatusInternalServerError {
|
||||
return res, resp.StatusCode, NewAPIError(res)
|
||||
return res, resp.StatusCode, CreateAPIError(res)
|
||||
}
|
||||
|
||||
if c.Debug {
|
||||
log.Printf("API Response: %s", res)
|
||||
c.writeLog("API Response: %s", res)
|
||||
}
|
||||
|
||||
return res, resp.StatusCode, nil
|
||||
|
@ -89,7 +104,6 @@ func (c *Client) PostRequest(
|
|||
)
|
||||
|
||||
prefix := "/api/v5"
|
||||
apiError := &APIError{}
|
||||
|
||||
if len(contType) > 0 {
|
||||
contentType = contType[0]
|
||||
|
@ -111,7 +125,7 @@ func (c *Client) PostRequest(
|
|||
req.Header.Set("X-API-KEY", c.Key)
|
||||
|
||||
if c.Debug {
|
||||
log.Printf("API Request: %s %s", uri, c.Key)
|
||||
c.writeLog("API Request: %s %s", uri, c.Key)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
|
@ -120,8 +134,8 @@ func (c *Client) PostRequest(
|
|||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusInternalServerError {
|
||||
apiError.ErrorMsg = fmt.Sprintf("HTTP request error. Status code: %d.\n", resp.StatusCode)
|
||||
return res, resp.StatusCode, apiError
|
||||
return res, resp.StatusCode, CreateGenericAPIError(
|
||||
fmt.Sprintf("HTTP request error. Status code: %d.", resp.StatusCode))
|
||||
}
|
||||
|
||||
res, err = buildRawResponse(resp)
|
||||
|
@ -130,11 +144,11 @@ func (c *Client) PostRequest(
|
|||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest && resp.StatusCode < http.StatusInternalServerError {
|
||||
return res, resp.StatusCode, NewAPIError(res)
|
||||
return res, resp.StatusCode, CreateAPIError(res)
|
||||
}
|
||||
|
||||
if c.Debug {
|
||||
log.Printf("API Response: %s", res)
|
||||
c.writeLog("API Response: %s", res)
|
||||
}
|
||||
|
||||
return res, resp.StatusCode, nil
|
||||
|
@ -4034,7 +4048,7 @@ func (c *Client) TaskEdit(task Task, site ...string) (SuccessfulResponse, int, e
|
|||
json.Unmarshal(data, &resp)
|
||||
|
||||
if resp.Success == false {
|
||||
a := NewAPIError(data)
|
||||
a := CreateAPIError(data)
|
||||
return resp, status, a
|
||||
}
|
||||
|
||||
|
@ -4179,7 +4193,7 @@ func (c *Client) UserStatus(id int, status string) (SuccessfulResponse, int, err
|
|||
json.Unmarshal(data, &resp)
|
||||
|
||||
if resp.Success == false {
|
||||
return resp, st, NewAPIError(data)
|
||||
return resp, st, CreateAPIError(data)
|
||||
}
|
||||
|
||||
return resp, st, nil
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -494,7 +494,7 @@ func TestClient_CustomersUpload_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(460).
|
||||
BodyString(`{"success": false, "errorMsg": "Customers are loaded with errors"}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Customers are loaded with ErrorsList"}`)
|
||||
|
||||
data, _, err := c.CustomersUpload(customers)
|
||||
if err == nil {
|
||||
|
@ -631,7 +631,7 @@ func TestClient_CustomersFixExternalIds_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"id": "ID must be an integer"}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"id": "ID must be an integer"}}`)
|
||||
|
||||
data, _, err := c.CustomersFixExternalIds(customers)
|
||||
if err == nil {
|
||||
|
@ -690,7 +690,7 @@ func TestClient_CustomersHistory_Fail(t *testing.T) {
|
|||
Get("/customers/history").
|
||||
MatchParam("filter[startDate]", "2020-13-12").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
|
||||
data, _, err := c.CustomersHistory(f)
|
||||
if err == nil {
|
||||
|
@ -885,7 +885,7 @@ func TestClient_CorporateCustomersFixExternalIds_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"id": "ID must be an integer"}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"id": "ID must be an integer"}}`)
|
||||
|
||||
data, _, err := c.CorporateCustomersFixExternalIds(customers)
|
||||
if err == nil {
|
||||
|
@ -944,7 +944,7 @@ func TestClient_CorporateCustomersHistory_Fail(t *testing.T) {
|
|||
Get("/customers-corporate/history").
|
||||
MatchParam("filter[startDate]", "2020-13-12").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
|
||||
data, _, err := c.CorporateCustomersHistory(f)
|
||||
if err == nil {
|
||||
|
@ -1130,7 +1130,7 @@ func TestClient_CorporateCustomersUpload_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(460).
|
||||
BodyString(`{"success": false, "errorMsg": "Customers are loaded with errors"}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Customers are loaded with ErrorsList"}`)
|
||||
|
||||
data, _, err := c.CorporateCustomersUpload(customers)
|
||||
if err == nil {
|
||||
|
@ -1703,7 +1703,7 @@ func TestClient_NotesNotes_Fail(t *testing.T) {
|
|||
Get("/customers/notes").
|
||||
MatchParam("filter[createdAtFrom]", "2020-13-12").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[createdAtFrom]": "This value is not valid."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"children[createdAtFrom]": "This value is not valid."}}`)
|
||||
|
||||
data, _, err := c.CustomerNotes(NotesRequest{
|
||||
Filter: NotesFilter{CreatedAtFrom: "2020-13-12"},
|
||||
|
@ -1803,7 +1803,7 @@ func TestClient_NotesCreateDelete_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "errors": {"customer": "Set one of the following fields: id, externalId"}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "ErrorsList": {"customer": "Set one of the following fields: id, externalId"}}`)
|
||||
|
||||
data, _, err := c.CustomerNoteCreate(note)
|
||||
if err == nil {
|
||||
|
@ -1875,7 +1875,7 @@ func TestClient_OrdersOrders_Fail(t *testing.T) {
|
|||
Get("/orders").
|
||||
MatchParam("filter[attachments]", "7").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[attachments]": "SThis value is not valid."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"children[attachments]": "SThis value is not valid."}}`)
|
||||
|
||||
data, _, err := c.Orders(OrdersRequest{Filter: OrdersFilter{Attachments: 7}})
|
||||
if err == nil {
|
||||
|
@ -2166,7 +2166,7 @@ func TestClient_OrdersUpload_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(460).
|
||||
BodyString(`{"success": false, "errorMsg": "Orders are loaded with errors"}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Orders are loaded with ErrorsList"}`)
|
||||
|
||||
data, _, err := c.OrdersUpload(orders)
|
||||
if err == nil {
|
||||
|
@ -2395,7 +2395,7 @@ func TestClient_OrdersHistory_Fail(t *testing.T) {
|
|||
Get("/orders/history").
|
||||
MatchParam("filter[startDate]", "2020-13-12").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"children[startDate]": "Значение недопустимо."}}`)
|
||||
|
||||
data, _, err := c.OrdersHistory(OrdersHistoryRequest{Filter: OrdersHistoryFilter{StartDate: "2020-13-12"}})
|
||||
if err == nil {
|
||||
|
@ -2521,7 +2521,7 @@ func TestClient_PaymentCreateEditDelete_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "errors": {"order": "Set one of the following fields: id, externalId, number"}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "ErrorsList": {"order": "Set one of the following fields: id, externalId, number"}}`)
|
||||
|
||||
data, _, err := c.OrderPaymentCreate(f)
|
||||
if err == nil {
|
||||
|
@ -2743,7 +2743,7 @@ func TestClient_TaskChange_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Task is not loaded", "errors": {"performerId": "This value should not be blank."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Task is not loaded", "ErrorsList": {"performerId": "This value should not be blank."}}`)
|
||||
|
||||
data, _, err := c.TaskEdit(f)
|
||||
if err == nil {
|
||||
|
@ -2792,7 +2792,7 @@ func TestClient_UsersUsers_Fail(t *testing.T) {
|
|||
MatchParam("filter[active]", "3").
|
||||
MatchParam("page", "1").
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "errors": {"active": "he value you selected is not a valid choice."}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the input parameters", "ErrorsList": {"active": "he value you selected is not a valid choice."}}`)
|
||||
|
||||
data, _, err := c.Users(UsersRequest{Filter: UsersFilter{Active: 3}, Page: 1})
|
||||
if err == nil {
|
||||
|
@ -3629,7 +3629,7 @@ func TestClient_Courier_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(400).
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "errors": {"firstName": "Specify the first name"}}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Errors in the entity format", "ErrorsList": {"firstName": "Specify the first name"}}`)
|
||||
|
||||
data, st, err := c.CourierCreate(Courier{})
|
||||
if err == nil {
|
||||
|
@ -5793,7 +5793,7 @@ func TestClient_CostsUpload_Fail(t *testing.T) {
|
|||
MatchType("url").
|
||||
BodyString(p.Encode()).
|
||||
Reply(460).
|
||||
BodyString(`{"success": false, "errorMsg": "Costs are loaded with errors"}`)
|
||||
BodyString(`{"success": false, "errorMsg": "Costs are loaded with ErrorsList"}`)
|
||||
|
||||
data, _, err := c.CostsUpload(costsUpload)
|
||||
if err == nil {
|
138
error.go
Normal file
138
error.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var missingParameterMatcher = regexp.MustCompile(`^Parameter \'([\w\]\[\_\-]+)\' is missing$`)
|
||||
var (
|
||||
// ErrMissingCredentials will be returned if no API key was provided to the API.
|
||||
ErrMissingCredentials = NewAPIError(`apiKey is missing`)
|
||||
// ErrInvalidCredentials will be returned if provided API key is invalid.
|
||||
ErrInvalidCredentials = NewAPIError(`wrong "apiKey" value`)
|
||||
// ErrAccessDenied will be returned in case of "Access denied" error.
|
||||
ErrAccessDenied = NewAPIError("access denied")
|
||||
// ErrAccountDoesNotExist will be returned if target system does not exist.
|
||||
ErrAccountDoesNotExist = NewAPIError("account does not exist")
|
||||
// ErrValidation will be returned in case of validation errors.
|
||||
ErrValidation = NewAPIError("validation error")
|
||||
// ErrMissingParameter will be returned if parameter is missing.
|
||||
// Underlying error messages list will contain parameter name in the "Name" key.
|
||||
ErrMissingParameter = NewAPIError("missing parameter")
|
||||
// ErrGeneric will be returned if error cannot be classified as one of the errors above.
|
||||
ErrGeneric = NewAPIError("API error")
|
||||
)
|
||||
|
||||
// APIErrorsList struct.
|
||||
type APIErrorsList map[string]string
|
||||
|
||||
// APIError returns when an API error was occurred.
|
||||
type APIError interface {
|
||||
error
|
||||
withWrapped(error) APIError
|
||||
withErrors(APIErrorsList) APIError
|
||||
Unwrap() error
|
||||
Errors() APIErrorsList
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
ErrorMsg string `json:"errorMsg,omitempty"`
|
||||
ErrorsList APIErrorsList `json:"errors,omitempty"`
|
||||
wrapped error
|
||||
}
|
||||
|
||||
// CreateAPIError from the provided response data. Different error types will be returned depending on the response,
|
||||
// all of them can be matched using errors.Is. APi errors will always implement APIError interface.
|
||||
func CreateAPIError(dataResponse []byte) error {
|
||||
a := &apiError{}
|
||||
|
||||
if len(dataResponse) > 0 && dataResponse[0] == '<' {
|
||||
return ErrAccountDoesNotExist
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(dataResponse, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var found APIError
|
||||
switch a.ErrorMsg {
|
||||
case `"apiKey" is missing.`:
|
||||
found = ErrMissingCredentials
|
||||
case `Wrong "apiKey" value.`:
|
||||
found = ErrInvalidCredentials
|
||||
case "Access denied.":
|
||||
found = ErrAccessDenied
|
||||
case "Account does not exist.":
|
||||
found = ErrAccountDoesNotExist
|
||||
case "Errors in the entity format":
|
||||
fallthrough
|
||||
case "Validation error":
|
||||
found = ErrValidation
|
||||
default:
|
||||
if param, ok := asMissingParameterErr(a.ErrorMsg); ok {
|
||||
return a.withWrapped(ErrMissingParameter).withErrors(APIErrorsList{"Name": param})
|
||||
}
|
||||
found = ErrGeneric
|
||||
}
|
||||
|
||||
result := NewAPIError(a.ErrorMsg).withWrapped(found)
|
||||
if len(a.ErrorsList) > 0 {
|
||||
return result.withErrors(a.ErrorsList)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// CreateGenericAPIError for the situations when API response cannot be processed, but response was actually received.
|
||||
func CreateGenericAPIError(message string) APIError {
|
||||
return NewAPIError(message).withWrapped(ErrGeneric)
|
||||
}
|
||||
|
||||
// NewAPIError returns API error with the provided message.
|
||||
func NewAPIError(message string) APIError {
|
||||
return &apiError{ErrorMsg: message}
|
||||
}
|
||||
|
||||
// asMissingParameterErr returns true if "Parameter {{}} is missing" error message is provided.
|
||||
func asMissingParameterErr(message string) (string, bool) {
|
||||
matches := missingParameterMatcher.FindAllStringSubmatch(message, -1)
|
||||
if len(matches) == 1 && len(matches[0]) == 2 {
|
||||
return matches[0][1], true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Error returns errorMsg field from the response.
|
||||
func (e *apiError) Error() string {
|
||||
return e.ErrorMsg
|
||||
}
|
||||
|
||||
// Unwrap returns wrapped error. It is usually one of the predefined types like ErrGeneric or ErrValidation.
|
||||
// It can be used directly, but it's main purpose is to make errors matchable via errors.Is call.
|
||||
func (e *apiError) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
// Errors returns errors field from the response.
|
||||
func (e *apiError) Errors() APIErrorsList {
|
||||
return e.ErrorsList
|
||||
}
|
||||
|
||||
// withError is an ErrorMsg setter.
|
||||
func (e *apiError) withError(message string) APIError {
|
||||
e.ErrorMsg = message
|
||||
return e
|
||||
}
|
||||
|
||||
// withWrapped is a wrapped setter.
|
||||
func (e *apiError) withWrapped(err error) APIError {
|
||||
e.wrapped = err
|
||||
return e
|
||||
}
|
||||
|
||||
// withErrors is an ErrorsList setter.
|
||||
func (e *apiError) withErrors(m APIErrorsList) APIError {
|
||||
e.ErrorsList = m
|
||||
return e
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func TestFailure_ApiErrorsSlice(t *testing.T) {
|
||||
|
@ -19,15 +18,14 @@ func TestFailure_ApiErrorsSlice(t *testing.T) {
|
|||
"1": "Test error",
|
||||
}
|
||||
|
||||
var expEr *APIError
|
||||
e := NewAPIError(b)
|
||||
e := CreateAPIError(b)
|
||||
|
||||
if xerrors.As(e, &expEr) {
|
||||
if eq := reflect.DeepEqual(expEr.Errors, expected); eq != true {
|
||||
if errors.Is(e, ErrGeneric) {
|
||||
if eq := reflect.DeepEqual(expected, e.(APIError).Errors()); eq != true {
|
||||
t.Errorf("%+v", eq)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error must be type of APIError: %v", e)
|
||||
t.Errorf("Error must be type of ErrGeneric: %v", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,14 +39,12 @@ func TestFailure_ApiErrorsMap(t *testing.T) {
|
|||
"test": "Test error",
|
||||
}
|
||||
|
||||
var expEr *APIError
|
||||
e := NewAPIError(b)
|
||||
|
||||
if xerrors.As(e, &expEr) {
|
||||
if eq := reflect.DeepEqual(expEr.Errors, expected); eq != true {
|
||||
e := CreateAPIError(b)
|
||||
if errors.Is(e, ErrGeneric) {
|
||||
if eq := reflect.DeepEqual(expected, e.(APIError).Errors()); eq != true {
|
||||
t.Errorf("%+v", eq)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error must be type of APIError: %v", e)
|
||||
t.Errorf("Error must be type of ErrGeneric: %v", e)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
// CustomersFilter type.
|
||||
type CustomersFilter struct {
|
5
go.mod
5
go.mod
|
@ -1,12 +1,9 @@
|
|||
module github.com/retailcrm/api-client-go
|
||||
module github.com/retailcrm/api-client-go/v2
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.1.0
|
||||
github.com/joho/godotenv v1.3.0
|
||||
golang.org/x/net v0.0.0-20191021144547-ec77196f6094 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/h2non/gentleman.v1 v1.0.4 // indirect
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
)
|
||||
|
|
7
go.sum
7
go.sum
|
@ -8,14 +8,7 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
|||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg=
|
||||
gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8=
|
||||
gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8=
|
||||
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=
|
||||
|
|
25
log.go
Normal file
25
log.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package retailcrm
|
||||
|
||||
// BasicLogger provides basic functionality for logging.
|
||||
type BasicLogger interface {
|
||||
Printf(string, ...interface{})
|
||||
}
|
||||
|
||||
// DebugLogger can be used to easily wrap any logger with Debugf method into the BasicLogger instance.
|
||||
type DebugLogger interface {
|
||||
Debugf(string, ...interface{})
|
||||
}
|
||||
|
||||
type debugLoggerAdapter struct {
|
||||
logger DebugLogger
|
||||
}
|
||||
|
||||
// DebugLoggerAdapter returns BasicLogger that calls underlying DebugLogger.Debugf.
|
||||
func DebugLoggerAdapter(logger DebugLogger) BasicLogger {
|
||||
return &debugLoggerAdapter{logger: logger}
|
||||
}
|
||||
|
||||
// Printf data in the log using DebugLogger.Debugf.
|
||||
func (l *debugLoggerAdapter) Printf(format string, v ...interface{}) {
|
||||
l.logger.Debugf(format, v...)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
// CustomerRequest type.
|
||||
type CustomerRequest struct {
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
// SuccessfulResponse type.
|
||||
type SuccessfulResponse struct {
|
||||
|
@ -20,7 +20,7 @@ type OrderCreateResponse struct {
|
|||
// OperationResponse type.
|
||||
type OperationResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Errors map[string]string `json:"errors,omitempty"`
|
||||
Errors map[string]string `json:"ErrorsList,omitempty"`
|
||||
}
|
||||
|
||||
// VersionResponse return available API versions.
|
||||
|
@ -31,7 +31,7 @@ type VersionResponse struct {
|
|||
|
||||
// CredentialResponse return available API methods.
|
||||
type CredentialResponse struct {
|
||||
Success bool `json:"success,omitempty"`
|
||||
Success bool `json:"success,omitempty"`
|
||||
// deprecated
|
||||
Credentials []string `json:"credentials,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -19,6 +19,7 @@ type Client struct {
|
|||
Key string
|
||||
Debug bool
|
||||
httpClient *http.Client
|
||||
logger BasicLogger
|
||||
}
|
||||
|
||||
// Pagination type.
|
|
@ -1,4 +1,4 @@
|
|||
package v5
|
||||
package retailcrm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
32
v5/error.go
32
v5/error.go
|
@ -1,32 +0,0 @@
|
|||
package v5
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// APIErrorsList struct.
|
||||
type APIErrorsList map[string]string
|
||||
|
||||
// APIError struct.
|
||||
type APIError struct {
|
||||
SuccessfulResponse
|
||||
ErrorMsg string `json:"errorMsg,omitempty"`
|
||||
Errors APIErrorsList `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
func (e *APIError) Error() string {
|
||||
return e.ErrorMsg
|
||||
}
|
||||
|
||||
func NewAPIError(dataResponse []byte) error {
|
||||
a := &APIError{}
|
||||
|
||||
if len(dataResponse) > 0 && dataResponse[0] == '<' {
|
||||
a.ErrorMsg = "Account does not exist."
|
||||
return a
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(dataResponse, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
Loading…
Add table
Reference in a new issue