package onpoint import ( "errors" "fmt" "net/http" "net/url" "strconv" "strings" "time" "git.selly.red/Selly-Modules/natsio" "git.selly.red/Selly-Modules/natsio/model" "git.selly.red/Selly-Modules/natsio/subject" "git.selly.red/Selly-Modules/3pl/util/httputil" "git.selly.red/Selly-Modules/3pl/util/pjson" ) // Client ... type Client struct { env ENV apiKey string secretKey string natsClient natsio.Server } // NewClient generate OnPoint client func NewClient(env ENV, apiKey, secretKey string, nc natsio.Server) (*Client, error) { if apiKey == "" { return nil, errors.New("onpoint: cannot init with empty api key") } return &Client{ env: env, apiKey: apiKey, secretKey: secretKey, natsClient: nc, }, nil } // CreateOrder ... func (c *Client) CreateOrder(p CreateOrderRequest) (*CreateOrderResponse, error) { url := c.getBaseURL() + apiPathCreateOrder natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: url, Method: http.MethodPost, Data: pjson.ToJSONString(p), }, } var ( r model.CommunicationHttpResponse errRes Error dataRes struct { Code string `json:"code"` Data CreateOrderResponse `json:"data"` } ) if err := c.requestHttpViaNats(natsPayload, &r); err != nil { return nil, err } res := r.Response if res == nil { return nil, fmt.Errorf("onpoint.Client.CreateOrder: empty_response") } if res.StatusCode >= http.StatusBadRequest { if err := r.ParseResponseData(&errRes); err != nil { return nil, fmt.Errorf("onpoint.Client.CreateOrder: parse_response_err: %v. Reason: %s", err, res.Body) } return nil, errRes } if err := r.ParseResponseData(&dataRes); err != nil { return nil, fmt.Errorf("onpoint.Client.CreateOrder: parse_response_data: %v. Raw response: %s", err, res.Body) } return &dataRes.Data, nil } // UpdateDelivery ... func (c *Client) UpdateDelivery(p UpdateOrderDeliveryRequest) (*UpdateOrderDeliveryResponse, error) { url := c.getBaseURL() + apiPathUpdateDelivery natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: url, Method: http.MethodPost, Data: pjson.ToJSONString(p), }, } var ( r model.CommunicationHttpResponse errRes Error dataRes struct { Data UpdateOrderDeliveryResponse `json:"data"` } ) if err := c.requestHttpViaNats(natsPayload, &r); err != nil { return nil, err } res := r.Response if res == nil { return nil, fmt.Errorf("onpoint.Client.UpdateDelivery: empty_response") } if res.StatusCode >= http.StatusBadRequest { if err := r.ParseResponseData(&errRes); err != nil { return nil, fmt.Errorf("onpoint.Client.UpdateDelivery: parse_response_err: %v", err) } return nil, errRes } if err := r.ParseResponseData(&dataRes); err != nil { return nil, fmt.Errorf("onpoint.Client.UpdateDelivery: parse_response_data: %v", err) } return &dataRes.Data, nil } // CancelOrder ... func (c *Client) CancelOrder(p CancelOrderRequest) (*CancelOrderResponse, error) { url := c.getBaseURL() + apiPathCancelOrder natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: url, Method: http.MethodPost, Data: pjson.ToJSONString(p), }, } var ( r model.CommunicationHttpResponse errRes Error dataRes struct { Data CancelOrderResponse `json:"data"` } ) if err := c.requestHttpViaNats(natsPayload, &r); err != nil { return nil, err } res := r.Response if res == nil { return nil, fmt.Errorf("onpoint.Client.CancelOrder: empty_response") } if res.StatusCode >= http.StatusBadRequest { if err := r.ParseResponseData(&errRes); err != nil { return nil, fmt.Errorf("onpoint.Client.CancelOrder: parse_response_err: %v", err) } return nil, errRes } if err := r.ParseResponseData(&dataRes); err != nil { return nil, fmt.Errorf("onpoint.Client.CancelOrder: parse_response_data: %v", err) } return &dataRes.Data, nil } // GetInventories ... func (c *Client) GetInventories(req ListInventoriesReq) (*ListInventoriesRes, error) { url := c.getBaseURL() + apiPathGetInventories q := map[string]string{} if !req.UpdatedFrom.IsZero() { q["updated_from"] = req.UpdatedFrom.Format(TimeLayout) } if !req.UpdatedTo.IsZero() { q["updated_to"] = req.UpdatedTo.Format(TimeLayout) } if len(req.SKUList) > 0 { q["sku_list"] = strings.Join(req.SKUList, ",") } if req.Page > 0 { q["page"] = strconv.Itoa(req.Page) } if req.Size > 0 { q["size"] = strconv.Itoa(req.Size) } natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: url, Method: http.MethodGet, Query: q, }, } var ( r model.CommunicationHttpResponse errRes Error dataRes ListInventoriesRes ) if err := c.requestHttpViaNats(natsPayload, &r); err != nil { return nil, err } res := r.Response if res == nil { return nil, fmt.Errorf("onpoint.Client.GetInventories: empty_response") } if res.StatusCode >= http.StatusBadRequest { if err := r.ParseResponseData(&errRes); err != nil { return nil, fmt.Errorf("onpoint.Client.GetInventories: parse_response_err: %v", err) } return nil, errRes } if err := r.ParseResponseData(&dataRes); err != nil { return nil, fmt.Errorf("onpoint.Client.GetInventories: parse_response_data: %v", err) } return &dataRes, nil } func (c *Client) requestHttpViaNats(data model.CommunicationRequestHttp, res interface{}) error { ec, err := c.natsClient.NewJSONEncodedConn() if err != nil { return fmt.Errorf("onpoint: request via nats %v", err) } u, err := url.ParseRequestURI(data.Payload.URL) if err != nil { return fmt.Errorf("onpoint: request via nats %v", err) } q := u.Query() for k, v := range data.Payload.Query { q.Set(k, v) } u.RawQuery = q.Encode() now := time.Now().Unix() ts := strconv.FormatInt(now, 10) arr := []string{ u.RawQuery, data.Payload.Data, ts, } s := strings.Join(arr, ".") // sign data sign := hashSHA256AndUppercase(s, c.secretKey) data.Payload.Header = map[string]string{ headerXAPIKey: c.apiKey, headerXSignature: sign, headerXTimestamp: ts, httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON, } data.Payload.Query = map[string]string{} data.Payload.URL = u.String() return ec.Request(subject.Communication.RequestHTTP, data, res) } func (c *Client) getBaseURL() string { return baseURLENVMapping[c.env] }