3pl/partnerapi/kiotviet/kiotviet.go

301 lines
8.6 KiB
Go

package kiotviet
import (
"fmt"
"log"
"net/http"
"net/url"
"strconv"
"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"
)
const (
logTarget = "kiotviet"
)
type Client struct {
clientID string
secretKey string
retailer string
natsClient natsio.Server
auth *AuthRes
authExpireTime time.Time
}
func New(clientID, secretKey, retailer string, natsClient natsio.Server) (*Client, error) {
if clientID == "" || secretKey == "" || retailer == "" {
return nil, fmt.Errorf("kiotviet: cannot create client with empty info")
}
return &Client{
clientID: clientID,
secretKey: secretKey,
retailer: retailer,
natsClient: natsClient,
}, nil
}
func (c *Client) GetProductOnHands(req ListProductOnHandsReq) (*ListProductOnHandsRes, error) {
apiURL := c.getURL(apiPathListProductOnHands)
query := map[string]string{
"orderBy": req.OrderBy,
"lastModifiedFrom": req.LastModifiedFrom,
}
if req.PageSize > 0 {
query["pageSize"] = strconv.Itoa(req.PageSize)
}
if req.CurrentItem > 0 {
query["currentItem"] = strconv.Itoa(req.CurrentItem)
}
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodGet,
Query: query,
Header: c.getRequestHeader(),
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.GetProductOnHands - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.GetProductOnHands - requestHttpViaNats bad request %s", res.Body)
}
var data ListProductOnHandsRes
if err = r.ParseResponseData(&data); err != nil {
return nil, fmt.Errorf("kiotviet.Client.GetProductOnHands - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) GetBranches(req ListBranchesReq) (*ListBranchesRes, error) {
apiURL := c.getURL(apiPathListBranches)
query := map[string]string{
"orderBy": req.OrderBy,
"lastModifiedFrom": req.LastModifiedFrom,
"orderDirection": req.OrderDirection,
"includeRemoveIds": req.IncludeRemoveIDs,
}
if req.PageSize > 0 {
query["pageSize"] = strconv.Itoa(req.PageSize)
}
if req.CurrentItem > 0 {
query["currentItem"] = strconv.Itoa(req.CurrentItem)
}
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodGet,
Query: query,
Header: c.getRequestHeader(),
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.GetBranches - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.GetBranches - requestHttpViaNats bad request %s", res.Body)
}
var data ListBranchesRes
if err = r.ParseResponseData(&data); err != nil {
return nil, fmt.Errorf("kiotviet.Client.GetBranches - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) ListWebhooks(req ListWebhookReq) (*ListWebhookRes, error) {
apiURL := c.getURL(apiPathListWebhook)
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodGet,
Header: c.getRequestHeader(),
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.ListWebhooks - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.ListWebhooks - requestHttpViaNats bad request %s", res.Body)
}
var data ListWebhookRes
if err = r.ParseResponseData(&data); err != nil {
return nil, fmt.Errorf("kiotviet.Client.ListWebhooks - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) RegisterWebhook(req RegisterWebhookReq) (*RegisterWebhookRes, error) {
apiURL := c.getURL(apiPathRegisterWebhook)
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodPost,
Data: pjson.ToJSONString(req),
Header: c.getRequestHeader(),
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.RegisterWebhook - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.RegisterWebhook - requestHttpViaNats bad request %s", res.Body)
}
var data RegisterWebhookRes
if err = r.ParseResponseData(&data); err != nil {
return nil, fmt.Errorf("kiotviet.Client.RegisterWebhook - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) UnregisterWebhook(req UnregisterWebhookReq) (*UnregisterWebhookRes, error) {
apiURL := c.getURL(fmt.Sprintf(apiPathUnregisterWebhook, req.ID))
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodDelete,
Header: c.getRequestHeader(),
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.UnregisterWebhook - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.UnregisterWebhook - requestHttpViaNats bad request %s", res.Body)
}
var data UnregisterWebhookRes
if err = r.ParseResponseData(&data.Success); err != nil {
return nil, fmt.Errorf("kiotviet.Client.UnregisterWebhook - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) Auth() (*AuthRes, error) {
v := url.Values{}
v.Add("scopes", "PublicApi.Access")
v.Add("grant_type", "client_credentials")
v.Add("client_id", c.clientID)
v.Add("client_secret", c.secretKey)
body := v.Encode()
header := map[string]string{
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationURLEncoded,
}
apiURL := baseURLTokenProd + apiPathAuth
natsPayload := model.CommunicationRequestHttp{
ResponseImmediately: true,
Payload: model.HttpRequest{
URL: apiURL,
Method: http.MethodPost,
Data: body,
Header: header,
},
LogTarget: logTarget,
}
r, err := c.requestHttpViaNats(natsPayload)
if err != nil {
log.Printf("kiotviet.Client.Auth - requestHttpViaNats: %v, %s\n", err, apiURL)
return nil, err
}
res := r.Response
if res.StatusCode >= http.StatusBadRequest {
return nil, fmt.Errorf("kiotviet.Client.Auth - requestHttpViaNats %s", res.Body)
}
var data AuthRes
if err = r.ParseResponseData(&data); err != nil {
return nil, fmt.Errorf("kiotviet.Client.Auth - requestHttpViaNats parse response %v, %s", err, res.Body)
}
return &data, nil
}
func (c *Client) getURL(path string) string {
return baseURLProd + path
}
func (c *Client) requestHttpViaNats(data model.CommunicationRequestHttp) (*model.CommunicationHttpResponse, error) {
b := pjson.ToBytes(data)
msg, err := c.natsClient.Request(subject.Communication.RequestHTTP, b)
if err != nil {
return nil, fmt.Errorf("kiotviet.Client.requestHttpViaNats err: %v, url %s", err, data.Payload.URL)
}
var r model.CommunicationHttpResponse
if err = pjson.Unmarshal(msg.Data, &r); err != nil {
return nil, fmt.Errorf("kiotviet.Client.requestHttpViaNats parse data err: %v, url %s, data %s", err, data.Payload.URL, string(msg.Data))
}
if r.Response == nil {
return nil, fmt.Errorf("kiotviet.Client.requestHttpViaNats empty reponse")
}
return &r, nil
}
func (c *Client) getRequestHeader() map[string]string {
m := map[string]string{
httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON,
"Retailer": c.retailer,
}
token, err := c.getToken()
if err != nil {
log.Printf("kiotviet.Client.getToken err %v\n", err)
} else {
m["Authorization"] = fmt.Sprintf("Bearer %s", token)
}
return m
}
func (c *Client) getToken() (string, error) {
auth := c.auth
if auth != nil && c.authExpireTime.After(time.Now()) {
return auth.AccessToken, nil
}
data, err := c.Auth()
if err != nil {
return "", err
}
c.auth = data
d := time.Duration(data.ExpiresIn) * time.Second
if d.Minutes() > 30 {
d -= 30 * time.Minute
}
c.authExpireTime = time.Now().Add(d)
return data.AccessToken, nil
}