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 }