3pl/partnerapi/tnc/tnc.go

315 lines
9.2 KiB
Go
Raw Normal View History

2022-08-15 10:28:35 +00:00
package tnc
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/Selly-Modules/logger"
"github.com/Selly-Modules/natsio"
2022-08-18 10:58:22 +00:00
"github.com/Selly-Modules/natsio/model"
2022-08-15 10:28:35 +00:00
"github.com/nats-io/nats.go"
2022-08-18 10:58:22 +00:00
"github.com/Selly-Modules/3pl/constant"
"github.com/Selly-Modules/3pl/util/pjson"
2022-08-15 10:28:35 +00:00
)
// Client ...
type Client struct {
// Auth info
realm string
clientID string
clientSecret string
env ENV
natsClient natsio.Server
token string
tokenExpireAt time.Time
}
// NewClient ...
func NewClient(env ENV, clientID, clientSecret, realm string, natsClient natsio.Server) (*Client, error) {
if env != EnvProd && env != EnvStaging {
return nil, fmt.Errorf("tnc.NewClient: invalid_env %s", env)
}
return &Client{
realm: realm,
clientID: clientID,
clientSecret: clientSecret,
token: "",
env: env,
natsClient: natsClient,
}, nil
}
// CreateOutboundRequest ...
func (c *Client) CreateOutboundRequest(p OutboundRequestPayload) (*OutboundRequestRes, error) {
apiURL := c.getBaseURL() + apiPathCreateOutboundRequest
2022-08-18 10:58:22 +00:00
natsPayload := model.CommunicationRequestHttp{
2022-08-15 10:28:35 +00:00
ResponseImmediately: true,
2022-08-18 10:58:22 +00:00
Payload: model.HttpRequest{
2022-08-15 10:28:35 +00:00
URL: apiURL,
2022-08-18 10:58:22 +00:00
Method: http.MethodPost,
2022-08-19 08:37:27 +00:00
Data: pjson.ToJSONString([]OutboundRequestPayload{p}),
2022-08-15 10:28:35 +00:00
Header: c.getRequestHeader(),
},
}
msg, err := c.requestHttpViaNats(natsPayload)
if err != nil {
logger.Error("tnc.Client.CreateOutboundRequest - requestHttpViaNats", logger.LogData{
"err": err.Error(),
"payload": natsPayload,
})
return nil, err
}
var (
2022-08-18 10:58:22 +00:00
r model.CommunicationHttpResponse
2022-08-15 10:28:35 +00:00
errRes ErrRes
dataRes []OutboundRequestRes
)
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: parse_data %v", err)
}
res := r.Response
if res == nil {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: empty_response")
}
if res.StatusCode >= http.StatusBadRequest {
if err = r.ParseResponseData(&errRes); err != nil {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: parse_response_err: %v", err)
}
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: failed code %s, message %s", errRes.Code, errRes.ErrorMessage)
}
if err = r.ParseResponseData(&dataRes); err != nil {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: parse_response_data: %v", err)
}
if len(dataRes) == 0 {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: empty_result")
}
2022-08-18 10:58:22 +00:00
item := &dataRes[0]
e := item.Error
if e != nil {
return nil, fmt.Errorf("tnc.Client.CreateOutboundRequest: failed, code %s - message %s", e.Code, e.ErrorMessage)
}
return item, err
}
2022-08-15 10:28:35 +00:00
2022-08-18 10:58:22 +00:00
// UpdateOutboundRequestLogisticInfo ...
func (c *Client) UpdateOutboundRequestLogisticInfo(p UpdateORLogisticInfoPayload) error {
apiURL := c.getBaseURL() + fmt.Sprintf(apiPathUpdateLogisticInfoOutboundRequest, p.OrID)
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodPost,
Header: c.getRequestHeader(),
},
}
msg, err := c.requestHttpViaNats(natsPayload)
if err != nil {
logger.Error("tnc.Client.UpdateOutboundRequestLogisticInfo - requestHttpViaNats", logger.LogData{
"err": err.Error(),
"payload": natsPayload,
})
return err
}
var (
r model.CommunicationHttpResponse
errRes ErrRes
)
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return fmt.Errorf("tnc.Client.UpdateOutboundRequestLogisticInfo: parse_data %v", err)
}
res := r.Response
if res == nil {
return fmt.Errorf("tnc.Client.UpdateOutboundRequestLogisticInfo: empty_response")
}
if res.StatusCode >= http.StatusBadRequest {
if err = r.ParseResponseData(&errRes); err != nil {
return fmt.Errorf("tnc.Client.UpdateOutboundRequestLogisticInfo: parse_response_err: %v", err)
}
return fmt.Errorf("tnc.Client.UpdateOutboundRequestLogisticInfo: failed code %s, message %s", errRes.Code, errRes.ErrorMessage)
}
return nil
2022-08-15 10:28:35 +00:00
}
// GetOutboundRequestByID ...
func (c *Client) GetOutboundRequestByID(requestID int) (*OutboundRequestInfo, error) {
apiURL := c.getBaseURL() + fmt.Sprintf(apiPathGetOutboundRequest, requestID)
2022-08-18 10:58:22 +00:00
natsPayload := model.CommunicationRequestHttp{
2022-08-15 10:28:35 +00:00
ResponseImmediately: true,
2022-08-18 10:58:22 +00:00
Payload: model.HttpRequest{
2022-08-15 10:28:35 +00:00
URL: apiURL,
Method: http.MethodGet,
Header: c.getRequestHeader(),
},
}
msg, err := c.requestHttpViaNats(natsPayload)
if err != nil {
logger.Error("tnc.Client.GetOutboundRequestByID - requestHttpViaNats", logger.LogData{
"err": err.Error(),
"payload": natsPayload,
})
return nil, err
}
var (
2022-08-18 10:58:22 +00:00
r model.CommunicationHttpResponse
2022-08-15 10:28:35 +00:00
errRes ErrRes
outboundRequest OutboundRequestInfo
)
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return nil, fmt.Errorf("tnc.Client.GetOutboundRequestByID: parse_data %v", err)
}
res := r.Response
if res == nil {
return nil, fmt.Errorf("tnc.Client.GetOutboundRequestByID: empty_response")
}
if res.StatusCode >= http.StatusBadRequest {
if err = r.ParseResponseData(&errRes); err != nil {
return nil, fmt.Errorf("tnc.Client.GetOutboundRequestByID: parse_response_err: %v", err)
}
return nil, fmt.Errorf("tnc.Client.GetOutboundRequestByID: failed code %s, message %s", errRes.Code, errRes.ErrorMessage)
}
2022-08-19 08:37:27 +00:00
if err = r.ParseResponseData(&outboundRequest); err != nil {
2022-08-15 10:28:35 +00:00
return nil, fmt.Errorf("tnc.Client.GetOutboundRequestByID: parse_response_data: %v", err)
}
return &outboundRequest, nil
}
// CancelOutboundRequest ...
2022-08-18 10:58:22 +00:00
func (c *Client) CancelOutboundRequest(requestID int, note string) error {
2022-08-15 10:28:35 +00:00
apiURL := c.getBaseURL() + fmt.Sprintf(apiPathCancelOutboundRequest, requestID)
2022-08-18 10:58:22 +00:00
data := map[string]string{"note": note}
natsPayload := model.CommunicationRequestHttp{
2022-08-15 10:28:35 +00:00
ResponseImmediately: true,
2022-08-18 10:58:22 +00:00
Payload: model.HttpRequest{
2022-08-15 10:28:35 +00:00
URL: apiURL,
Method: http.MethodPost,
Header: c.getRequestHeader(),
2022-08-18 10:58:22 +00:00
Data: pjson.ToJSONString(data),
2022-08-15 10:28:35 +00:00
},
}
msg, err := c.requestHttpViaNats(natsPayload)
if err != nil {
logger.Error("tnc.Client.CancelOutboundRequest - requestHttpViaNats", logger.LogData{
"err": err.Error(),
"payload": natsPayload,
})
return err
}
var (
2022-08-18 10:58:22 +00:00
r model.CommunicationHttpResponse
2022-08-15 10:28:35 +00:00
errRes ErrRes
)
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return fmt.Errorf("tnc.Client.CancelOutboundRequest: parse_data %v", err)
}
res := r.Response
if res == nil {
return fmt.Errorf("tnc.Client.CancelOutboundRequest: empty_response")
}
if res.StatusCode >= http.StatusBadRequest {
if err = r.ParseResponseData(&errRes); err != nil {
return fmt.Errorf("tnc.Client.CancelOutboundRequest: parse_response_err: %v", err)
}
return fmt.Errorf("tnc.Client.CancelOutboundRequest: failed code %s, message %s", errRes.Code, errRes.ErrorMessage)
}
return nil
}
func (c *Client) auth() (*authRes, error) {
v := url.Values{}
v.Add("realm", c.realm)
v.Add("grant_type", "client_credentials")
v.Add("client_id", c.clientID)
v.Add("client_secret", c.clientSecret)
body := v.Encode()
header := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
}
apiURL := baseURLAuthENVMapping[c.env] + fmt.Sprintf(apiPathAuth, c.realm)
2022-08-18 10:58:22 +00:00
natsPayload := model.CommunicationRequestHttp{
2022-08-15 10:28:35 +00:00
ResponseImmediately: true,
2022-08-18 10:58:22 +00:00
Payload: model.HttpRequest{
2022-08-15 10:28:35 +00:00
URL: apiURL,
Method: http.MethodPost,
Data: body,
Header: header,
},
}
msg, err := c.requestHttpViaNats(natsPayload)
if err != nil {
logger.Error("tnc.Client.auth - requestHttpViaNats", logger.LogData{
"err": err.Error(),
"payload": natsPayload,
})
return nil, err
}
var (
2022-08-18 10:58:22 +00:00
r model.CommunicationHttpResponse
2022-08-15 10:28:35 +00:00
errRes ErrRes
data authRes
)
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return nil, fmt.Errorf("tnc.Client.auth: parse_data %v", err)
}
res := r.Response
if res == nil {
return nil, fmt.Errorf("tnc.Client.auth: empty_response")
}
if res.StatusCode >= http.StatusBadRequest {
if err = r.ParseResponseData(&errRes); err != nil {
return nil, fmt.Errorf("tnc.Client.auth: parse_response_err: %v", err)
}
return nil, fmt.Errorf("tnc.Client.auth: failed code %s, message %s", errRes.Code, errRes.ErrorMessage)
}
2022-08-19 08:37:27 +00:00
if err = r.ParseResponseData(&data); err != nil {
2022-08-15 10:28:35 +00:00
return nil, fmt.Errorf("tnc.Client.auth: parse_response_data: %v", err)
}
return &data, nil
}
func (c *Client) getRequestHeader() map[string]string {
m := map[string]string{
"Content-Type": "application/json",
}
token, err := c.getToken()
if err != nil {
logger.Error("tnc.Client.getToken", logger.LogData{"err": err.Error()})
} else {
m["Authorization"] = fmt.Sprintf("Bearer %s", token)
}
return m
}
2022-08-18 10:58:22 +00:00
func (c *Client) requestHttpViaNats(data model.CommunicationRequestHttp) (*nats.Msg, error) {
2022-08-15 10:28:35 +00:00
s := constant.NatsCommunicationSubjectRequestHTTP
b := pjson.ToBytes(data)
return c.natsClient.Request(s, b)
}
func (c *Client) getBaseURL() string {
return baseURLENVMapping[c.env]
}
func (c *Client) getToken() (string, error) {
if c.token != "" || c.tokenExpireAt.After(time.Now()) {
return c.token, nil
}
data, err := c.auth()
if err != nil {
return "", err
}
c.token = data.AccessToken
d := time.Duration(data.ExpiresIn) * time.Second
if d.Minutes() > 30 {
d -= 30 * time.Minute
}
c.tokenExpireAt = time.Now().Add(d)
return c.token, nil
}