diff --git a/calculator-models.go b/calculator-models.go index b381cbb..aa04efc 100644 --- a/calculator-models.go +++ b/calculator-models.go @@ -25,7 +25,7 @@ type ServiceResp struct { //GetCostReq Cost calculation on tariffs with priority request type GetCostReq struct { - securableJSON + securable Version *string `json:"version"` SenderCityID *int `json:"senderCityId"` ReceiverCityID *int `json:"receiverCityId"` diff --git a/calculator-service.go b/calculator-service.go index 48b5eca..d564924 100644 --- a/calculator-service.go +++ b/calculator-service.go @@ -2,46 +2,44 @@ package cdek import ( "bytes" + "context" "encoding/json" - "errors" "fmt" - "io/ioutil" + "github.com/hashicorp/go-multierror" "net/http" ) //CalculateDelivery Cost calculation on tariffs with priority. -func (c Client) CalculateDelivery(req GetCostReq) (*GetCostRespResult, error) { +func (c Client) CalculateDelivery(ctx context.Context, req GetCostReq) (*GetCostRespResult, error) { req.setAuth(c.auth) - reqByte, err := json.Marshal(req) + + payload, err := json.Marshal(&req) if err != nil { return nil, err } - resp, err := http.Post(c.calculatorURL, jsonContentType, bytes.NewReader(reqByte)) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, c.calculatorURL, bytes.NewReader(payload)) if err != nil { return nil, err } + r.Header.Add("Content-Type", jsonContentType) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var getCostResp getCostResp - err = json.Unmarshal(body, &getCostResp) + resp, err := jsonReq[getCostResp](r) if err != nil { return nil, err } - if getCostResp.ErrorResp != nil { - var errorMsg string - for _, err := range getCostResp.ErrorResp { - errorMsg += fmt.Sprintf("Error code: %s, error text: %s \n", *err.ErrorCode, *err.Msg) + if resp.ErrorResp != nil { + var errs error + for _, err := range resp.ErrorResp { + errs = multierror.Append( + errs, + fmt.Errorf("error code: %s, error text: %s", *err.ErrorCode, *err.Msg), + ) } - return nil, errors.New(errorMsg) + return nil, errs } - return &getCostResp.Result, nil + return &resp.Result, nil } diff --git a/calculator-service_test.go b/calculator-service_test.go index 2c38276..b8d701b 100644 --- a/calculator-service_test.go +++ b/calculator-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "encoding/json" "io/ioutil" "math" @@ -24,7 +25,7 @@ func calculateDeliveryGetMockServer() *httptest.Server { Msg: strLink("Указанная вами версия API не поддерживается"), }) } - if *getCostReq.Secure == "" || *getCostReq.AuthLogin == "" || *getCostReq.DateExecute == "" { + if *getCostReq.Secure == "" || *getCostReq.Account == "" || *getCostReq.Date == "" { errorsResp = append(errorsResp, Error{ ErrorCode: strLink("2"), Msg: strLink("Ошибка авторизации"), @@ -191,12 +192,12 @@ func TestClient_CalculateDelivery(t *testing.T) { SenderCityID: nil, ReceiverCityID: intLink(2), TariffID: intLink(3), - Goods: []*Good{ + Goods: []*Good{ { Weight: math.Inf(1), }, }, - Services: nil, + Services: nil, }, }, nil, @@ -214,10 +215,11 @@ func TestClient_CalculateDelivery(t *testing.T) { true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.args.client - got, err := cl.CalculateDelivery(tt.args.req) + got, err := cl.CalculateDelivery(ctx, tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("CalculateDelivery() error = %v, wantErr %v", err, tt.wantErr) return @@ -233,7 +235,8 @@ func ExampleClient_CalculateDelivery() { client := NewClient("https://integration.edu.cdek.ru/") client.SetAuth("z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd", "w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq") - result, err := client.CalculateDelivery(*NewGetCostReq(61208, 2108, 10)) + ctx := context.TODO() + result, err := client.CalculateDelivery(ctx, *NewGetCostReq(61208, 2108, 10)) _, _ = result, err } diff --git a/city-service.go b/city-service.go index 2dc6d4e..609a0ed 100755 --- a/city-service.go +++ b/city-service.go @@ -1,13 +1,10 @@ package cdek import ( - "encoding/json" - "io/ioutil" + "context" "net/http" "net/url" "path" - - "github.com/hashicorp/go-multierror" ) const ( @@ -15,49 +12,23 @@ const ( ) //GetCities This method is used to load detailed information on cities. -func (c Client) GetCities(filter map[CityFilter]string) (*GetCitiesResp, error) { +func (c Client) GetCities(ctx context.Context, filter map[CityFilter]string) (*GetCitiesResp, error) { serverURL, err := url.Parse(c.apiURL) if err != nil { return nil, err } - serverURL.Path = path.Join(serverURL.Path, citiesURL) - - queryString := serverURL.Query() - for filterKey, value := range filter { - queryString.Set(string(filterKey), value) + qs := serverURL.Query() + for k, v := range filter { + qs.Set(string(k), v) } - serverURL.RawQuery = queryString.Encode() - - reqURL := serverURL.String() + serverURL.Path = path.Join(serverURL.Path, citiesURL) + serverURL.RawQuery = qs.Encode() - resp, err := http.Get(reqURL) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, serverURL.String(), nil) if err != nil { return nil, err } - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var cities GetCitiesResp - err = json.Unmarshal(body, &cities) - if err != nil { - var alertResponse AlertResponse - err = json.Unmarshal(body, &alertResponse) - if err != nil { - return nil, err - } - - multiError := &multierror.Error{} - for _, alert := range alertResponse.Alerts { - multiError = multierror.Append(alert) - } - - return nil, multiError.ErrorOrNil() - } - - return &cities, nil + return jsonReq[GetCitiesResp](req) } diff --git a/city-service_test.go b/city-service_test.go index 53c544a..51cbd6c 100644 --- a/city-service_test.go +++ b/city-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "net/http" "net/http/httptest" "reflect" @@ -178,10 +179,11 @@ func TestClient_GetCities(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.args.client - got, err := cl.GetCities(tt.args.filter) + got, err := cl.GetCities(ctx, tt.args.filter) if (err != nil) != tt.wantErr { t.Errorf("GetCities() error = %v, wantErr %v", err, tt.wantErr) return @@ -197,7 +199,8 @@ func ExampleClient_GetCities() { client := NewClient("https://integration.edu.cdek.ru/") client.SetAuth("z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd", "w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq") - result, err := client.GetCities(map[CityFilter]string{ + ctx := context.TODO() + result, err := client.GetCities(ctx, map[CityFilter]string{ CityFilterPage: "3", }) diff --git a/client.go b/client.go index e0d826b..fff198d 100755 --- a/client.go +++ b/client.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "crypto/md5" "encoding/hex" "time" @@ -23,14 +24,14 @@ type ServiceAccessСonfigurator interface { type ServiceProvider interface { ServiceAccessСonfigurator - CalculateDelivery(req GetCostReq) (*GetCostRespResult, error) - GetCities(filter map[CityFilter]string) (*GetCitiesResp, error) - GetPvzList(filter map[PvzListFilter]string) ([]*Pvz, error) - GetRegions(filter map[RegionFilter]string) (*GetRegionsResp, error) - RegisterOrder(req RegisterOrderReq) (*RegisterOrderResp, error) - UpdateOrder(req UpdateOrderReq) (*UpdateOrderResp, error) - DeleteOrder(req DeleteOrderReq) (*DeleteOrderResp, error) - GetStatusReport(statusReportReq StatusReport) (*StatusReportResp, error) + CalculateDelivery(ctx context.Context, req GetCostReq) (*GetCostRespResult, error) + GetCities(ctx context.Context, filter map[CityFilter]string) (*GetCitiesResp, error) + GetPvzList(ctx context.Context, filter map[PvzListFilter]string) ([]*Pvz, error) + GetRegions(ctx context.Context, filter map[RegionFilter]string) (*GetRegionsResp, error) + RegisterOrder(ctx context.Context, req RegisterOrderReq) (*RegisterOrderResp, error) + UpdateOrder(ctx context.Context, req UpdateOrderReq) (*UpdateOrderResp, error) + DeleteOrder(ctx context.Context, req DeleteOrderReq) (*DeleteOrderResp, error) + GetStatusReport(ctx context.Context, statusReportReq StatusReport) (*StatusReportResp, error) } //Client SDK Client configuration diff --git a/delete-order-models.go b/delete-order-models.go index 2b45b7f..2419b62 100644 --- a/delete-order-models.go +++ b/delete-order-models.go @@ -4,7 +4,7 @@ import "encoding/xml" //DeleteOrderReq request structure for deleting order from CDEK type DeleteOrderReq struct { - securableXML + securable XMLName xml.Name `xml:"DeleteRequest"` Number *string `xml:"Number,attr"` OrderCount *int `xml:"OrderCount,attr"` diff --git a/delete-order-service.go b/delete-order-service.go index e444277..bb75919 100755 --- a/delete-order-service.go +++ b/delete-order-service.go @@ -1,8 +1,8 @@ package cdek import ( + "context" "encoding/xml" - "io/ioutil" "net/http" "net/url" "path" @@ -16,51 +16,44 @@ const ( ) //DeleteOrder The method is designed to cancel/delete an order at the client's initiative. -func (c Client) DeleteOrder(req DeleteOrderReq) (*DeleteOrderResp, error) { - req.setAuth(c.auth) - reqByte, err := xml.Marshal(req) - +func (c Client) DeleteOrder(ctx context.Context, req DeleteOrderReq) (*DeleteOrderResp, error) { + serverURL, err := url.Parse(c.apiURL) if err != nil { return nil, err } + serverURL.Path = path.Join(serverURL.Path, deleteOrderURL) - data := make(url.Values) - data.Add("xml_request", string(reqByte)) + req.setAuth(c.auth) - serverURL, err := url.Parse(c.apiURL) + xmlBytes, err := xml.Marshal(req) if err != nil { return nil, err } - serverURL.Path = path.Join(serverURL.Path, deleteOrderURL) - reqURL := serverURL.String() + data := &url.Values{} + data.Add("xml_request", string(xmlBytes)) - resp, err := http.Post(reqURL, urlFormEncoded, strings.NewReader(data.Encode())) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, serverURL.String(), strings.NewReader(data.Encode())) if err != nil { return nil, err } + r.Header.Add("Content-Type", urlFormEncoded) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var deleteOrderResp DeleteOrderResp - err = xml.Unmarshal(body, &deleteOrderResp) + resp, err := xmlReq[DeleteOrderResp](r) if err != nil { return nil, err } - multiError := &multierror.Error{} - for _, o := range deleteOrderResp.Order { + var errs error + for _, o := range resp.Order { if o.IsErroneous() { - multiError = multierror.Append(o.GetError()) + errs = multierror.Append(errs, o.GetError()) } } - if multiError.Len() > 0 { - return nil, multiError.ErrorOrNil() + + if errs != nil { + return nil, errs } - return &deleteOrderResp, nil + return resp, nil } diff --git a/delete-order-service_test.go b/delete-order-service_test.go index 6da47b6..16d51a2 100644 --- a/delete-order-service_test.go +++ b/delete-order-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "encoding/json" "encoding/xml" "net/http" @@ -187,10 +188,11 @@ func TestClient_DeleteOrder(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.DeleteOrder(tt.args.req) + got, err := cl.DeleteOrder(ctx, tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("DeleteOrder() error = %v, wantErr %v", err, tt.wantErr) return @@ -208,7 +210,8 @@ func ExampleClient_DeleteOrder() { client := NewClient("https://integration.edu.cdek.ru/") client.SetAuth("z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd", "w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq") - result, err := client.DeleteOrder(*NewDeleteOrderReq( + ctx := context.TODO() + result, err := client.DeleteOrder(ctx, *NewDeleteOrderReq( "number-soOEl0", 1, *NewDeleteOrder().SetNumber("number-soOEl0"), diff --git a/go.mod b/go.mod index 5fa06ff..e4d498f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/vseinstrumentiru/cdek -go 1.12 +go 1.18 require github.com/hashicorp/go-multierror v1.0.0 + +require github.com/hashicorp/errwrap v1.0.0 // indirect diff --git a/pvzlist-service.go b/pvzlist-service.go index b14ad3d..206b610 100755 --- a/pvzlist-service.go +++ b/pvzlist-service.go @@ -1,8 +1,7 @@ package cdek import ( - "encoding/xml" - "io/ioutil" + "context" "net/http" "net/url" "path" @@ -13,12 +12,11 @@ const ( ) //GetPvzList The method is used to load the list of active pickup points, from which the client can pick up its order. -func (c Client) GetPvzList(filter map[PvzListFilter]string) ([]*Pvz, error) { +func (c Client) GetPvzList(ctx context.Context, filter map[PvzListFilter]string) ([]*Pvz, error) { serverURL, err := url.Parse(c.apiURL) if err != nil { return nil, err } - serverURL.Path = path.Join(serverURL.Path, pvzListURL) queryString := serverURL.Query() @@ -27,24 +25,16 @@ func (c Client) GetPvzList(filter map[PvzListFilter]string) ([]*Pvz, error) { } serverURL.RawQuery = queryString.Encode() - reqURL := serverURL.String() - - resp, err := http.Get(reqURL) + r, err := http.NewRequestWithContext(ctx, http.MethodGet, serverURL.String(), nil) if err != nil { return nil, err } + r.Header.Add("Content-Type", urlFormEncoded) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var pvzList pvzList - err = xml.Unmarshal(body, &pvzList) + resp, err := xmlReq[pvzList](r) if err != nil { return nil, err } - return pvzList.Pvz, nil + return resp.Pvz, nil } diff --git a/pvzlist-service_test.go b/pvzlist-service_test.go index aa53a06..a655b8b 100644 --- a/pvzlist-service_test.go +++ b/pvzlist-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "encoding/json" "net/http" "net/http/httptest" @@ -192,10 +193,11 @@ func TestClient_GetPvzList(t *testing.T) { true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.GetPvzList(tt.args.filter) + got, err := cl.GetPvzList(ctx, tt.args.filter) if (err != nil) != tt.wantErr { t.Errorf("Client.GetPvzList() error = %v, wantErr %v", err, tt.wantErr) return @@ -213,7 +215,8 @@ func ExampleClient_GetPvzList() { client := NewClient("https://integration.edu.cdek.ru/") client.SetAuth("z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd", "w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq") - result, err := client.GetPvzList(map[PvzListFilter]string{ + ctx := context.TODO() + result, err := client.GetPvzList(ctx, map[PvzListFilter]string{ PvzListFilterCityID: "44", }) diff --git a/region-service.go b/region-service.go index 75cb837..a8451b4 100755 --- a/region-service.go +++ b/region-service.go @@ -1,13 +1,10 @@ package cdek import ( - "encoding/json" - "io/ioutil" + "context" "net/http" "net/url" "path" - - "github.com/hashicorp/go-multierror" ) const ( @@ -15,7 +12,7 @@ const ( ) //GetRegions This method is used to load detailed information on regions. -func (c Client) GetRegions(filter map[RegionFilter]string) (*GetRegionsResp, error) { +func (c Client) GetRegions(ctx context.Context, filter map[RegionFilter]string) (*GetRegionsResp, error) { serverURL, err := url.Parse(c.apiURL) if err != nil { return nil, err @@ -29,35 +26,10 @@ func (c Client) GetRegions(filter map[RegionFilter]string) (*GetRegionsResp, err } serverURL.RawQuery = queryString.Encode() - reqURL := serverURL.String() - - resp, err := http.Get(reqURL) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, serverURL.String(), nil) if err != nil { return nil, err } - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var regions GetRegionsResp - err = json.Unmarshal(body, ®ions) - if err != nil { - var alertResponse AlertResponse - err = json.Unmarshal(body, &alertResponse) - if err != nil { - return nil, err - } - - multiError := &multierror.Error{} - for _, alert := range alertResponse.Alerts { - multiError = multierror.Append(alert) - } - - return nil, multiError.ErrorOrNil() - } - - return ®ions, nil + return jsonReq[GetRegionsResp](req) } diff --git a/region-service_test.go b/region-service_test.go index f517a4e..b8a65c9 100644 --- a/region-service_test.go +++ b/region-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "net/http" "net/http/httptest" "reflect" @@ -103,10 +104,11 @@ func TestClient_GetRegions(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.GetRegions(tt.args.filter) + got, err := cl.GetRegions(ctx, tt.args.filter) if (err != nil) != tt.wantErr { t.Errorf("Client.GetRegions() error = %v, wantErr %v", err, tt.wantErr) return @@ -158,7 +160,8 @@ func ExampleClient_GetRegions() { client := NewClient("https://integration.edu.cdek.ru/") client.SetAuth("z9GRRu7FxmO53CQ9cFfI6qiy32wpfTkd", "w24JTCv4MnAcuRTx0oHjHLDtyt3I6IBq") - result, err := client.GetRegions(map[RegionFilter]string{ + ctx := context.TODO() + result, err := client.GetRegions(ctx, map[RegionFilter]string{ RegionFilterPage: "3", }) diff --git a/register-order-models.go b/register-order-models.go index bc2bc42..9835480 100644 --- a/register-order-models.go +++ b/register-order-models.go @@ -9,7 +9,7 @@ import ( //RegisterOrderReq Order registration request type RegisterOrderReq struct { - securableXML + securable XMLName xml.Name `xml:"DeliveryRequest"` Number *string `xml:"Number,attr"` OrderCount *int `xml:"OrderCount,attr"` diff --git a/register-order-service.go b/register-order-service.go index cfbf316..bdfab9b 100755 --- a/register-order-service.go +++ b/register-order-service.go @@ -1,8 +1,8 @@ package cdek import ( + "context" "encoding/xml" - "io/ioutil" "net/http" "net/url" "path" @@ -16,7 +16,7 @@ const ( ) //RegisterOrder This method is used to register orders to be delivered to clients. -func (c Client) RegisterOrder(req RegisterOrderReq) (*RegisterOrderResp, error) { +func (c Client) RegisterOrder(ctx context.Context, req RegisterOrderReq) (*RegisterOrderResp, error) { req.setAuth(c.auth) reqByte, err := xml.Marshal(req) @@ -33,39 +33,28 @@ func (c Client) RegisterOrder(req RegisterOrderReq) (*RegisterOrderResp, error) } serverURL.Path = path.Join(serverURL.Path, registerOrderURL) - reqURL := serverURL.String() - resp, err := http.Post(reqURL, urlFormEncoded, strings.NewReader(data.Encode())) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, serverURL.String(), strings.NewReader(data.Encode())) if err != nil { return nil, err } + r.Header.Add("Content-Type", urlFormEncoded) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var registerOrderResp RegisterOrderResp - err = xml.Unmarshal(body, ®isterOrderResp) + resp, err := xmlReq[RegisterOrderResp](r) if err != nil { return nil, err } - multiError := &multierror.Error{} - for _, o := range registerOrderResp.Order { + var errs error + for _, o := range resp.Order { if o.IsErroneous() { - multiError = multierror.Append(o.GetError()) + errs = multierror.Append(errs, o.GetError()) } } - for _, c := range registerOrderResp.Call { - if c.IsErroneous() { - multiError = multierror.Append(c.Error) - } - } - if multiError.Len() > 0 { - return nil, multiError.ErrorOrNil() + + if errs != nil { + return nil, errs } - return ®isterOrderResp, nil + return resp, nil } diff --git a/register-order-service_test.go b/register-order-service_test.go index c8acc46..41f1aa7 100644 --- a/register-order-service_test.go +++ b/register-order-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "net/http" "net/http/httptest" "reflect" @@ -148,10 +149,11 @@ func TestClient_RegisterOrder(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.RegisterOrder(tt.args.req) + got, err := cl.RegisterOrder(ctx, tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("Client.RegisterOrder() error = %v, wantErr %v", err, tt.wantErr) return @@ -248,7 +250,8 @@ func ExampleClient_RegisterOrder() { ), ) - result, err := client.RegisterOrder(*NewDeliveryRequest("soOEl", 1, exampleOrder)) + ctx := context.TODO() + result, err := client.RegisterOrder(ctx, *NewDeliveryRequest("soOEl", 1, exampleOrder)) _, _ = result, err } diff --git a/secure.go b/secure.go index 7309ceb..c9758d3 100644 --- a/secure.go +++ b/secure.go @@ -1,19 +1,13 @@ package cdek -type securableXML struct { - Account *string `xml:"Account,attr"` - Date *string `xml:"Date,attr"` - Secure *string `xml:"Secure,attr"` -} - -type securableJSON struct { - AuthLogin *string `json:"authLogin,omitempty"` - Secure *string `json:"secure,omitempty"` - DateExecute *string `json:"dateExecute,omitempty"` +type securable struct { + Account *string `xml:"Account,attr" json:"authLogin,omitempty"` + Date *string `xml:"Date,attr" json:"dateExecute,omitempty"` + Secure *string `xml:"Secure,attr" json:"secure,omitempty"` } //TODO: there are some methods that MUST HAVE auth, need to handle this case -func (s *securableXML) setAuth(auth *auth) *securableXML { +func (s *securable) setAuth(auth *auth) *securable { if auth == nil { return s } @@ -26,17 +20,3 @@ func (s *securableXML) setAuth(auth *auth) *securableXML { return s } - -func (s *securableJSON) setAuth(auth *auth) *securableJSON { - if auth == nil { - return s - } - - s.AuthLogin = &auth.account - - date, sec := auth.encodedSecure() - s.DateExecute = &date - s.Secure = &sec - - return s -} diff --git a/secure_test.go b/secure_test.go index 029f92a..dac241e 100644 --- a/secure_test.go +++ b/secure_test.go @@ -26,7 +26,7 @@ func Test_securableJSON_setAuth(t *testing.T) { name string fields fields args args - want *securableJSON + want *securable }{ { name: "secure fields pass correct", @@ -37,25 +37,25 @@ func Test_securableJSON_setAuth(t *testing.T) { secure: "testSecure", }, }, - want: &securableJSON{ - AuthLogin: strLink("testAccount"), - Secure: &testSecureEncoded, - DateExecute: &now, + want: &securable{ + Account: strLink("testAccount"), + Secure: &testSecureEncoded, + Date: &now, }, }, { name: "empty auth", fields: fields{}, args: args{}, - want: &securableJSON{}, + want: &securable{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := &securableJSON{ - AuthLogin: tt.fields.AuthLogin, - Secure: tt.fields.Secure, - DateExecute: tt.fields.DateExecute, + s := &securable{ + Account: tt.fields.AuthLogin, + Secure: tt.fields.Secure, + Date: tt.fields.DateExecute, } if got := s.setAuth(tt.args.auth); !reflect.DeepEqual(got, tt.want) { t.Errorf("setAuth() = %v, want %v", got, tt.want) @@ -84,7 +84,7 @@ func Test_securableXML_setAuth(t *testing.T) { name string fields fields args args - want *securableXML + want *securable }{ { name: "secure fields pass correct", @@ -95,7 +95,7 @@ func Test_securableXML_setAuth(t *testing.T) { secure: testSecure, }, }, - want: &securableXML{ + want: &securable{ Account: &testAccount, Date: &now, Secure: &testSecureEncoded, @@ -105,12 +105,12 @@ func Test_securableXML_setAuth(t *testing.T) { name: "empty auth", fields: fields{}, args: args{}, - want: &securableXML{}, + want: &securable{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := &securableXML{ + s := &securable{ Account: tt.fields.Account, Date: tt.fields.Date, Secure: tt.fields.Secure, diff --git a/status-report-models.go b/status-report-models.go index 7caf4f5..8e09617 100644 --- a/status-report-models.go +++ b/status-report-models.go @@ -2,7 +2,7 @@ package cdek //StatusReport Order Status Report request type StatusReport struct { - securableXML + securable ShowHistory *int `xml:"ShowHistory,attr"` ShowReturnOrder *bool `xml:"ShowReturnOrder,attr"` ShowReturnOrderHistory *bool `xml:"ShowReturnOrderHistory,attr"` diff --git a/status-report-service.go b/status-report-service.go index 855ace0..a313397 100644 --- a/status-report-service.go +++ b/status-report-service.go @@ -1,8 +1,8 @@ package cdek import ( + "context" "encoding/xml" - "io/ioutil" "net/http" "net/url" "path" @@ -14,7 +14,7 @@ const ( ) //GetStatusReport This method is used to generate an order status report, including order change history. -func (c Client) GetStatusReport(statusReportReq StatusReport) (*StatusReportResp, error) { +func (c Client) GetStatusReport(ctx context.Context, statusReportReq StatusReport) (*StatusReportResp, error) { statusReportReq.setAuth(c.auth) reqByte, err := xml.Marshal(statusReportReq) if err != nil { @@ -30,27 +30,21 @@ func (c Client) GetStatusReport(statusReportReq StatusReport) (*StatusReportResp } serverURL.Path = path.Join(serverURL.Path, statusReportURL) - reqURL := serverURL.String() - resp, err := http.Post(reqURL, urlFormEncoded, strings.NewReader(data.Encode())) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, serverURL.String(), strings.NewReader(data.Encode())) if err != nil { return nil, err } + r.Header.Add("Content-Type", urlFormEncoded) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var statusReportResp StatusReportResp - err = xml.Unmarshal(body, &statusReportResp) + resp, err := xmlReq[StatusReportResp](r) if err != nil { return nil, err } - if statusReportResp.IsErroneous() { - return nil, statusReportResp.Error + + if resp.IsErroneous() { + return nil, resp.Error } - return &statusReportResp, nil + return resp, nil } diff --git a/status-report-service_test.go b/status-report-service_test.go index 89f3405..3c508ad 100644 --- a/status-report-service_test.go +++ b/status-report-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "encoding/json" "net/http" "net/http/httptest" @@ -133,10 +134,11 @@ func TestClient_GetStatusReport(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.GetStatusReport(tt.args.statusReportReq) + got, err := cl.GetStatusReport(ctx, tt.args.statusReportReq) if (err != nil) != tt.wantErr { t.Errorf("Client.GetStatusReport() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/update-order-models.go b/update-order-models.go index e810c2f..34e7c0b 100644 --- a/update-order-models.go +++ b/update-order-models.go @@ -4,7 +4,7 @@ import "encoding/xml" //UpdateOrderReq Order Change request structure type UpdateOrderReq struct { - securableXML + securable XMLName xml.Name `xml:"UpdateRequest"` Number *string `xml:"Number,attr"` OrderCount *int `xml:"OrderCount,attr"` diff --git a/update-order-service.go b/update-order-service.go index 47bad83..382f4c7 100755 --- a/update-order-service.go +++ b/update-order-service.go @@ -1,8 +1,8 @@ package cdek import ( + "context" "encoding/xml" - "io/ioutil" "net/http" "net/url" "path" @@ -16,7 +16,7 @@ const ( ) //UpdateOrder This method is used to change a created order. -func (c Client) UpdateOrder(req UpdateOrderReq) (*UpdateOrderResp, error) { +func (c Client) UpdateOrder(ctx context.Context, req UpdateOrderReq) (*UpdateOrderResp, error) { req.setAuth(c.auth) reqByte, err := xml.Marshal(req) @@ -33,34 +33,28 @@ func (c Client) UpdateOrder(req UpdateOrderReq) (*UpdateOrderResp, error) { } serverURL.Path = path.Join(serverURL.Path, updateOrderURL) - reqURL := serverURL.String() - resp, err := http.Post(reqURL, urlFormEncoded, strings.NewReader(data.Encode())) + r, err := http.NewRequestWithContext(ctx, http.MethodPost, serverURL.String(), strings.NewReader(data.Encode())) if err != nil { return nil, err } + r.Header.Add("Content-Type", urlFormEncoded) - defer func() { - _ = resp.Body.Close() - }() - - body, _ := ioutil.ReadAll(resp.Body) - - var updateOrderResp UpdateOrderResp - err = xml.Unmarshal(body, &updateOrderResp) + resp, err := xmlReq[UpdateOrderResp](r) if err != nil { return nil, err } - multiError := &multierror.Error{} - for _, o := range updateOrderResp.Order { + var errs error + for _, o := range resp.Order { if o.IsErroneous() { - multiError = multierror.Append(o.GetError()) + errs = multierror.Append(errs, o.GetError()) } } - if multiError.Len() > 0 { - return nil, multiError.ErrorOrNil() + + if errs != nil { + return nil, errs } - return &updateOrderResp, nil + return resp, nil } diff --git a/update-order-service_test.go b/update-order-service_test.go index 596c527..d47550e 100644 --- a/update-order-service_test.go +++ b/update-order-service_test.go @@ -1,6 +1,7 @@ package cdek import ( + "context" "net/http" "net/http/httptest" "reflect" @@ -113,10 +114,11 @@ func TestClient_UpdateOrder(t *testing.T) { wantErr: true, }, } + ctx := context.TODO() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cl := tt.fields.client - got, err := cl.UpdateOrder(tt.args.req) + got, err := cl.UpdateOrder(ctx, tt.args.req) if (err != nil) != tt.wantErr { t.Errorf("Client.UpdateOrder() error = %v, wantErr %v", err, tt.wantErr) return @@ -177,7 +179,8 @@ func ExampleClient_UpdateOrder() { ), ) - result, err := client.UpdateOrder(*NewUpdateOrderReq("soOEl", 1, *exampleOrderToUpdate)) + ctx := context.TODO() + result, err := client.UpdateOrder(ctx, *NewUpdateOrderReq("soOEl", 1, *exampleOrderToUpdate)) _, _ = result, err } diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..5618882 --- /dev/null +++ b/utils.go @@ -0,0 +1,49 @@ +package cdek + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "io/ioutil" + "net/http" +) + +func jsonReq[T any](req *http.Request) (*T, error) { + response, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer response.Body.Close() + + var s T + payload, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(payload, &s); err != nil { + return nil, fmt.Errorf("%s", payload) + } + + return &s, nil +} + +func xmlReq[T any](req *http.Request) (*T, error) { + response, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer response.Body.Close() + + var s T + payload, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + + if err := xml.Unmarshal(payload, &s); err != nil { + return nil, fmt.Errorf("%s", payload) + } + + return &s, nil +}