162 lines
3 KiB
Go
162 lines
3 KiB
Go
package api
|
|
|
|
import (
|
|
"net/url"
|
|
"time"
|
|
"log"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
DefaultContentType = "application/json"
|
|
DefaultAccept = DefaultContentType
|
|
DefaultCacheControl = "no-cache"
|
|
MaxEntitiesPerRequest = 250
|
|
)
|
|
|
|
type ClientOptions struct {
|
|
URL string `json:"url"`
|
|
RedirectURL string `json:"redirect_url"`
|
|
|
|
AuthCode string `json:"auth_code"`
|
|
|
|
ClientId string `json:"client_id"`
|
|
ClientSecret string `json:"client_secret"`
|
|
|
|
AccessToken string `json:"access_token"`
|
|
|
|
ExpirationAt time.Time `json:"expiration_at,omitempty"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
}
|
|
|
|
type Client struct {
|
|
*log.Logger
|
|
options ClientOptions
|
|
BaseURL *url.URL
|
|
secretStoreFilePath string
|
|
Debug bool
|
|
|
|
availableRequests int
|
|
mrps int
|
|
|
|
ticker *time.Ticker
|
|
req, reqNeed chan struct{}
|
|
|
|
requestsMade int64
|
|
}
|
|
|
|
type OAuthTokenResponse struct {
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
}
|
|
|
|
type TokenPair struct {
|
|
Access, Refresh string
|
|
}
|
|
|
|
func NewAPI(secretPath string) (*Client, error) {
|
|
client := &Client{
|
|
Logger: log.New(
|
|
os.Stdout,
|
|
"AmoCRM client: ",
|
|
log.Ldate | log.Ltime,
|
|
),
|
|
secretStoreFilePath: secretPath,
|
|
}
|
|
options, err := client.readSecret()
|
|
if err != nil {
|
|
return nil, NewErrorAPI(err)
|
|
}
|
|
|
|
if client.Debug {
|
|
client.Printf("ClientOptions: %v\n", options)
|
|
}
|
|
|
|
if options.URL == "" || options.RedirectURL == "" {
|
|
return nil, NewErrorAPI(ErrInvalidOptionsURL)
|
|
}
|
|
|
|
parsedURL, err := url.Parse(options.URL)
|
|
if err != nil {
|
|
return nil, NewErrorAPI(err)
|
|
}
|
|
|
|
client.BaseURL = parsedURL
|
|
client.options = options
|
|
|
|
var (
|
|
//pair *TokenPair
|
|
exchangeErr error
|
|
exchanged bool
|
|
)
|
|
if client.options.AccessToken == "" &&
|
|
client.options.RefreshToken == "" {
|
|
|
|
if client.options.ClientSecret == "" ||
|
|
client.options.ClientId == "" ||
|
|
client.options.AuthCode == "" {
|
|
return nil, NewErrorAPI(
|
|
ErrInvalidExchangeAuthOptions,
|
|
)
|
|
}
|
|
|
|
_, exchangeErr = client.ExchangeAuth()
|
|
exchanged = true
|
|
}
|
|
|
|
if (!exchanged || exchangeErr != nil) &&
|
|
options.RefreshToken != "" {
|
|
|
|
// Refreshing token before the work.
|
|
// Should think of how often should refresh
|
|
// the token. (see the ExpiresIn)
|
|
_, err = client.RefreshToken()
|
|
if err != nil {
|
|
return nil, NewErrorAPI(err)
|
|
}
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
// Set maximum requests per second.
|
|
func (client *Client) SetMRPS(rps int) *Client {
|
|
client.mrps = rps
|
|
client.req = make(chan struct{})
|
|
client.reqNeed = make(chan struct{})
|
|
client.ticker = time.NewTicker(
|
|
time.Second/time.Duration(rps),
|
|
)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <- client.reqNeed :
|
|
client.req <- struct{}{}
|
|
}
|
|
<-client.ticker.C
|
|
}
|
|
}()
|
|
return client
|
|
}
|
|
|
|
func (client *Client) waitInQueue() bool {
|
|
// Just leaving if MRPS is not set.
|
|
if client.mrps == 0 {
|
|
return false
|
|
}
|
|
|
|
client.reqNeed <- struct{}{}
|
|
<- client.req
|
|
|
|
return true
|
|
}
|
|
|
|
func (client *Client) finishRequest() {
|
|
}
|
|
|
|
func (client *Client) RequestsMade() int64 {
|
|
return client.requestsMade
|
|
}
|
|
|