package viettelffm import ( "errors" "fmt" "net/http" "net/url" "time" "git.selly.red/Selly-Modules/natsio" "git.selly.red/Selly-Modules/natsio/model" "git.selly.red/Selly-Modules/natsio/subject" tpl "git.selly.red/Selly-Modules/3pl" "git.selly.red/Selly-Modules/3pl/util/httputil" "git.selly.red/Selly-Modules/3pl/util/pjson" ) const ( baseURLStag = "https://stg-gw.viettelpost.vn" baseURLAuthStag = "https://stg-keycloak.viettelpost.vn" // TODO: update prod url baseURLProd = "" baseURLAuthProd = "" pathAuth = "/realms/wms/protocol/openid-connect/token" pathUpdateORLogisticInfo = "/wms-core/api/v1/obms/outbound-request/outbound-request-partner/%s/bill" pathCreateOR = "/wms-core/api/v1/obms/outbound-request/outbound-request-partner/hab" pathCancelOR = "/wms-core/api/v1/obms/outbound-request/cancel" logTarget = "viettel-ffm" ) var ( baseURLENVMapping = map[tpl.ENV]string{ tpl.EnvProd: baseURLProd, tpl.EnvStaging: baseURLStag, } ) type Client struct { env tpl.ENV username string password string natsClient natsio.Server authInfo *AuthRes authTokenExpireTime time.Time } // NewClient generate OnPoint client func NewClient(env tpl.ENV, user, pwd string, nc natsio.Server) (*Client, error) { if user == "" || pwd == "" { return nil, errors.New("viettelffm: cannot init with empty api key") } return &Client{ env: env, username: user, password: pwd, natsClient: nc, }, nil } func (c *Client) CreateOR(p ORPayload) (*ORResult, error) { apiURL := c.getBaseURL() + pathCreateOR token, err := c.getToken() if err != nil { return nil, err } natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: apiURL, Method: http.MethodPost, Data: pjson.ToJSONString(p), Header: map[string]string{ httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token), httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON, }, }, LogTarget: logTarget, } r, err := c.requestHttpViaNats(natsPayload) if err != nil { return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats %v", err) } res := r.Response if res.StatusCode >= http.StatusBadRequest { return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats %s", res.Body) } var data ORResult if err = r.ParseResponseData(&data); err != nil { return nil, fmt.Errorf("viettelffm.Client.CreateOR - requestHttpViaNats parse response %v, %s", err, res.Body) } return &data, nil } func (c *Client) UpdateORLogisticInfo(p UpdateLogisticInfoPayload) error { apiURL := c.getBaseURL() + pathUpdateORLogisticInfo token, err := c.getToken() if err != nil { return err } natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: apiURL, Method: http.MethodPut, Data: pjson.ToJSONString(p), Header: map[string]string{ httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token), httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON, }, }, LogTarget: logTarget, } r, err := c.requestHttpViaNats(natsPayload) if err != nil { return err } res := r.Response if res.StatusCode >= http.StatusBadRequest { return fmt.Errorf("viettelffm.Client.UpdateOutboundRequestLogisticInfo - requestHttpViaNats %s", res.Body) } return nil } func (c *Client) CancelOR(p CancelORPayload) error { apiURL := c.getBaseURL() + pathCancelOR token, err := c.getToken() if err != nil { return err } natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: apiURL, Method: http.MethodPost, Data: pjson.ToJSONString(p), Header: map[string]string{ httputil.HeaderKeyAuthorization: fmt.Sprintf("Bearer %s", token), httputil.HeaderKeyContentType: httputil.HeaderValueApplicationJSON, }, }, LogTarget: logTarget, } r, err := c.requestHttpViaNats(natsPayload) if err != nil { return err } res := r.Response if res.StatusCode >= http.StatusBadRequest { return fmt.Errorf("viettelffm.Client.CancelOR - requestHttpViaNats %s", res.Body) } return nil } // Auth ... func (c *Client) Auth() (*AuthRes, error) { v := url.Values{} v.Set("username", c.username) v.Set("password", c.password) v.Set("grant_type", "password") v.Set("client_id", "wms_account") b := v.Encode() header := map[string]string{ httputil.HeaderKeyContentType: httputil.HeaderValueApplicationURLEncoded, } bURL := baseURLAuthStag if c.env == tpl.EnvProd { bURL = baseURLAuthProd } api := bURL + pathAuth natsPayload := model.CommunicationRequestHttp{ ResponseImmediately: true, Payload: model.HttpRequest{ URL: api, Method: http.MethodPost, Data: b, Header: header, }, LogTarget: logTarget, } r, err := c.requestHttpViaNats(natsPayload) if err != nil { return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats: payload %+v, err %v\n", natsPayload, err) } res := r.Response if res.StatusCode >= http.StatusBadRequest { return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats %s", res.Body) } var data AuthRes if err = r.ParseResponseData(&data); err != nil { return nil, fmt.Errorf("viettelffm.Client.Auth - requestHttpViaNats parse response %v, %s", err, res.Body) } return &data, nil } func (c *Client) getToken() (string, error) { if c.authInfo == nil || time.Now().After(c.authTokenExpireTime) { auth, err := c.Auth() if err != nil { return "", err } c.authInfo = auth c.authTokenExpireTime = time.Now().Add(time.Duration(auth.ExpiresIn) * time.Second).Add(-time.Minute) } return c.authInfo.AccessToken, nil } func (c *Client) getBaseURL() string { return baseURLENVMapping[c.env] } 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("viettelffm.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("viettelffm.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("viettelffm.Client.requestHttpViaNats empty reponse") } return &r, nil }