From 22cbdd7fcfe01b6b9187489de62248fd24e5ba1c Mon Sep 17 00:00:00 2001 From: Ruslan Efanov Date: Fri, 9 Dec 2022 11:38:21 +0300 Subject: [PATCH] add parameter of failed entity in response for upload methods for orders and customers --- client.go | 70 ++++++++++++++++++++++++++++++++++++++++++------- client_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++------- response.go | 2 ++ types.go | 4 +++ 4 files changed, 127 insertions(+), 20 deletions(-) diff --git a/client.go b/client.go index 0e58345..65b15c9 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,10 @@ import ( "github.com/google/go-querystring/query" ) +// HTTPStatusUnknown can return for the method `/api/v5/customers/upload`, `/api/v5/customers-corporate/upload`, +// `/api/v5/orders/upload` +const HTTPStatusUnknown = 460 + // New initialize client. func New(url string, key string) *Client { return &Client{ @@ -670,6 +674,8 @@ func (c *Client) CustomerNoteDelete(id int) (SuccessfulResponse, int, error) { // CustomersUpload customers batch upload // +// # This method can return response together with error if http status is equal 460 +// // For more information see http://www.simla.com/docs/Developers/API/APIVersions/APIv5#post--api-v5-customers-upload // // Example: @@ -716,12 +722,16 @@ func (c *Client) CustomersUpload(customers []Customer, site ...string) (Customer fillSite(&p, site) data, status, err := c.PostRequest("/customers/upload", p) - if err != nil { + if err != nil && status != HTTPStatusUnknown { return resp, status, err } - err = json.Unmarshal(data, &resp) - if err != nil { + errJSON := json.Unmarshal(data, &resp) + if errJSON != nil { + return resp, status, errJSON + } + + if status == HTTPStatusUnknown { return resp, status, err } @@ -1145,6 +1155,8 @@ func (c *Client) CorporateCustomerNoteDelete(id int) (SuccessfulResponse, int, e // CorporateCustomersUpload corporate customers batch upload // +// # This method can return response together with error if http status is equal 460 +// // For more information see http://help.retailcrm.pro/Developers/ApiVersion5#post--api-v5-customers-corporate-upload // // Example: @@ -1187,12 +1199,16 @@ func (c *Client) CorporateCustomersUpload( fillSite(&p, site) data, status, err := c.PostRequest("/customers-corporate/upload", p) - if err != nil { + if err != nil && status != HTTPStatusUnknown { return resp, status, err } - err = json.Unmarshal(data, &resp) - if err != nil { + errJSON := json.Unmarshal(data, &resp) + if errJSON != nil { + return resp, status, err + } + + if status == HTTPStatusUnknown { return resp, status, err } @@ -2589,6 +2605,8 @@ func (c *Client) OrdersStatuses(request OrdersStatusesRequest) (OrdersStatusesRe // OrdersUpload batch orders uploading // +// # This method can return response together with error if http status is equal 460 +// // For more information see http://www.simla.com/docs/Developers/API/APIVersions/APIv5#post--api-v5-orders-upload // // Example: @@ -2635,12 +2653,16 @@ func (c *Client) OrdersUpload(orders []Order, site ...string) (OrdersUploadRespo fillSite(&p, site) data, status, err := c.PostRequest("/orders/upload", p) - if err != nil { + if err != nil && status != HTTPStatusUnknown { return resp, status, err } - err = json.Unmarshal(data, &resp) - if err != nil { + errJSON := json.Unmarshal(data, &resp) + if errJSON != nil { + return resp, status, err + } + + if status == HTTPStatusUnknown { return resp, status, err } @@ -6355,7 +6377,7 @@ func (c *Client) CreateProductsGroup(group ProductGroup) (ActionProductsGroupRes // ExternalID: "abc22", // } // -// data, status, err := client.EditProductsGroup("by", "125", "main", group) +// data, status, err := client.EditProductsGroup("id", "125", "main", group) // // if err != nil { // if apiErr, ok := retailcrm.AsAPIError(err); ok { @@ -6394,6 +6416,34 @@ func (c *Client) EditProductsGroup(by, id, site string, group ProductGroup) (Act return result, status, nil } +// GetOrderPlate receives a print form file for the order +// +// # Body of response is already closed +// +// For more information see https://help.retailcrm.ru/api_v5_ru.html#get--api-v5-orders-externalId-plates-plateId-print +// +// Example: +// +// var client = retailcrm.New("https://demo.url", "09jIJ") +// +// data, status, err := client.GetOrderPlate("id", "107", "main", 1) +// +// if err != nil { +// if apiErr, ok := retailcrm.AsAPIError(err); ok { +// log.Fatalf("http status: %d, %s", status, apiErr.String()) +// } +// +// log.Fatalf("http status: %d, error: %s", status, err) +// } +// +// if data != nil { +// fileData, err := io.ReadAll(data) +// if err != nil { +// return +// } +// +// log.Printf("%s", fileData) +// } func (c *Client) GetOrderPlate(by, orderID, site string, plateID int) (io.ReadCloser, int, error) { p := url.Values{ "by": {checkBy(by)}, diff --git a/client_test.go b/client_test.go index 863357c..e6eaaf6 100644 --- a/client_test.go +++ b/client_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "github.com/google/go-querystring/query" - "io" "io/ioutil" "log" "math/rand" @@ -505,7 +504,15 @@ func TestClient_CustomersUpload(t *testing.T) { MatchType("url"). BodyString(p.Encode()). Reply(200). - BodyString(`{"success": true}`) + BodyString(`{ + "success": true, + "uploadedCustomers": [ + { + "id": 136 + } + ], + "failedCustomers": [] + }`) data, status, err := c.CustomersUpload(customers) if err != nil { @@ -519,6 +526,8 @@ func TestClient_CustomersUpload(t *testing.T) { if data.Success != true { t.Errorf("%v", err) } + + assert.Equal(t, 136, data.UploadedCustomers[0].ID) } func TestClient_CustomersUpload_Fail(t *testing.T) { @@ -532,7 +541,7 @@ func TestClient_CustomersUpload_Fail(t *testing.T) { p := url.Values{ "customers": {string(str)}, } - // TODO ??? + gock.New(crmURL). Post("/api/v5/customers/upload"). MatchType("url"). @@ -556,7 +565,7 @@ func TestClient_CustomersUpload_Fail(t *testing.T) { } }`, iCodeFail)) - data, _, err := c.CustomersUpload(customers) + data, status, err := c.CustomersUpload(customers) if err == nil { t.Error("Error must be return") } @@ -564,6 +573,12 @@ func TestClient_CustomersUpload_Fail(t *testing.T) { if data.Success != false { t.Error(successFail) } + + assert.Equal(t, 132, data.UploadedCustomers[0].ID) + assert.Equal(t, HTTPStatusUnknown, status) + assert.Equal(t, fmt.Sprintf("%d", iCodeFail), data.FailedCustomers[0].ExternalID) + assert.Equal(t, "Customers are loaded with errors", err.Error()) + assert.Equal(t, "Something went wrong", err.(APIError).Errors()["managerId"]) } func TestClient_CustomersCombine(t *testing.T) { @@ -1176,7 +1191,7 @@ func TestClient_CorporateCustomersUpload(t *testing.T) { func TestClient_CorporateCustomersUpload_Fail(t *testing.T) { c := client() - customers := []CorporateCustomer{{ExternalID: strconv.Itoa(iCodeFail)}} + customers := []CorporateCustomer{{ExternalID: strconv.Itoa(iCodeFail), ManagerID: 100001}} defer gock.Off() @@ -1190,9 +1205,19 @@ func TestClient_CorporateCustomersUpload_Fail(t *testing.T) { MatchType("url"). BodyString(p.Encode()). Reply(460). - BodyString(`{"success": false, "errorMsg": "Customers are loaded with ErrorsList"}`) + BodyString(fmt.Sprintf(`{ + "success": false, + "uploadedCustomers": [], + "failedCustomers": [{ + "externalId": "%d" + }], + "errorMsg": "Customers are loaded with errors", + "errors": { + "managerId": "Something went wrong" + } + }`, iCodeFail)) - data, _, err := c.CorporateCustomersUpload(customers) + data, status, err := c.CorporateCustomersUpload(customers) if err == nil { t.Error("Error must be return") } @@ -1200,6 +1225,12 @@ func TestClient_CorporateCustomersUpload_Fail(t *testing.T) { if data.Success != false { t.Error(successFail) } + + assert.Empty(t, data.UploadedCustomers) + assert.Equal(t, HTTPStatusUnknown, status) + assert.Equal(t, fmt.Sprintf("%d", iCodeFail), data.FailedCustomers[0].ExternalID) + assert.Equal(t, "Customers are loaded with errors", err.Error()) + assert.Equal(t, "Something went wrong", err.(APIError).Errors()["managerId"]) } func TestClient_CorporateCustomer(t *testing.T) { @@ -2210,6 +2241,9 @@ func TestClient_OrdersUpload_Fail(t *testing.T) { LastName: fmt.Sprintf("Test_%s", RandomString(8)), ExternalID: strconv.Itoa(iCodeFail), Email: fmt.Sprintf("%s@example.com", RandomString(8)), + Items: []OrderItem{ + {Offer: Offer{ID: iCodeFail}}, + }, }, } @@ -2226,9 +2260,21 @@ func TestClient_OrdersUpload_Fail(t *testing.T) { MatchType("url"). BodyString(p.Encode()). Reply(460). - BodyString(`{"success": false, "errorMsg": "Orders are loaded with ErrorsList"}`) + BodyString(fmt.Sprintf(`{ + "success": false, + "uploadedOrders": [], + "failedOrders": [ + { + "externalId": "%d" + } + ], + "errorMsg": "Orders are loaded with errors", + "errors": [ + "items[0].offer.id: Offer with id %d not found." + ] + }`, iCodeFail, iCodeFail)) - data, _, err := c.OrdersUpload(orders) + data, status, err := c.OrdersUpload(orders) if err == nil { t.Error("Error must be return") } @@ -2236,6 +2282,11 @@ func TestClient_OrdersUpload_Fail(t *testing.T) { if data.Success != false { t.Error(successFail) } + + assert.Equal(t, HTTPStatusUnknown, status) + assert.Equal(t, fmt.Sprintf("%d", iCodeFail), data.FailedOrders[0].ExternalID) + assert.Equal(t, "Orders are loaded with errors", err.Error()) + assert.Equal(t, "items[0].offer.id: Offer with id 123123 not found.", err.(APIError).Errors()["0"]) } func TestClient_OrdersCombine(t *testing.T) { @@ -7635,7 +7686,7 @@ func TestClient_GetOrderPlate(t *testing.T) { "site": "main", }). Reply(200). - Body(io.NopCloser(strings.NewReader("PDF"))) + Body(ioutil.NopCloser(strings.NewReader("PDF"))) data, status, err := client().GetOrderPlate("id", "124", "main", 1) diff --git a/response.go b/response.go index 1c7eaf3..6ee0add 100644 --- a/response.go +++ b/response.go @@ -123,6 +123,7 @@ type CorporateCustomerChangeResponse CustomerChangeResponse type CustomersUploadResponse struct { Success bool `json:"success"` UploadedCustomers []IdentifiersPair `json:"uploadedCustomers,omitempty"` + FailedCustomers []ExternalID `json:"failedCustomers,omitempty"` } // CorporateCustomersUploadResponse type. @@ -167,6 +168,7 @@ type OrdersStatusesResponse struct { type OrdersUploadResponse struct { Success bool `json:"success"` UploadedOrders []IdentifiersPair `json:"uploadedOrders,omitempty"` + FailedOrders []ExternalID `json:"failedOrders,omitempty"` } // OrdersHistoryResponse type. diff --git a/types.go b/types.go index cf99fcf..1144f38 100644 --- a/types.go +++ b/types.go @@ -1427,3 +1427,7 @@ type SerializedLoyalty struct { Name string `json:"name"` ChargeRate float32 `json:"chargeRate"` } + +type ExternalID struct { + ExternalID string `json:"externalId,omitempty"` +}