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 }