github.com/polygon-io/client-go@v1.16.4/rest/client/client.go (about) 1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/go-resty/resty/v2" 10 "github.com/polygon-io/client-go/rest/encoder" 11 "github.com/polygon-io/client-go/rest/models" 12 ) 13 14 const clientVersion = "v1.16.0" 15 16 const ( 17 APIURL = "https://api.polygon.io" 18 DefaultRetryCount = 3 19 ) 20 21 // Client defines an HTTP client for the Polygon REST API. 22 type Client struct { 23 HTTP *resty.Client 24 encoder *encoder.Encoder 25 } 26 27 // New returns a new client with the specified API key and default settings. 28 func New(apiKey string) Client { 29 return newClient(apiKey, nil) 30 } 31 32 // NewWithClient returns a new client with the specified API key and a custom HTTP client. 33 func NewWithClient(apiKey string, hc *http.Client) Client { 34 return newClient(apiKey, hc) 35 } 36 37 func newClient(apiKey string, hc *http.Client) Client { 38 var c *resty.Client 39 if hc == nil { 40 c = resty.New() 41 } else { 42 c = resty.NewWithClient(hc) 43 } 44 45 c.SetBaseURL(APIURL) 46 c.SetAuthToken(apiKey) 47 c.SetRetryCount(DefaultRetryCount) 48 c.SetTimeout(10 * time.Second) 49 c.SetHeader("User-Agent", fmt.Sprintf("Polygon.io GoClient/%v", clientVersion)) 50 c.SetHeader("Accept-Encoding", "gzip") 51 52 return Client{ 53 HTTP: c, 54 encoder: encoder.New(), 55 } 56 } 57 58 // Call makes an API call based on the request params and options. The response is automatically unmarshaled. 59 func (c *Client) Call(ctx context.Context, method, path string, params, response any, opts ...models.RequestOption) error { 60 uri, err := c.encoder.EncodeParams(path, params) 61 if err != nil { 62 return err 63 } 64 return c.CallURL(ctx, method, uri, response, opts...) 65 } 66 67 // CallURL makes an API call based on a request URI and options. The response is automatically unmarshaled. 68 func (c *Client) CallURL(ctx context.Context, method, uri string, response any, opts ...models.RequestOption) error { 69 options := mergeOptions(opts...) 70 71 req := c.HTTP.R().SetContext(ctx) 72 if options.APIKey != nil { 73 req.SetAuthToken(*options.APIKey) 74 } 75 req.SetQueryParamsFromValues(options.QueryParams) 76 req.SetHeaderMultiValues(options.Headers) 77 req.SetResult(response).SetError(&models.ErrorResponse{}) 78 79 res, err := req.Execute(method, uri) 80 if err != nil { 81 return fmt.Errorf("failed to execute request: %w", err) 82 } else if res.IsError() { 83 errRes := res.Error().(*models.ErrorResponse) 84 errRes.StatusCode = res.StatusCode() 85 if errRes.RequestID == "" { 86 errRes.RequestID = res.Header().Get("X-Request-ID") 87 } 88 return errRes 89 } 90 91 if options.Trace { 92 fmt.Printf("Request URL: %s\n", uri) 93 sanitizedHeaders := req.Header 94 for k := range sanitizedHeaders { 95 if k == "Authorization" { 96 sanitizedHeaders[k] = []string{"REDACTED"} 97 } 98 } 99 fmt.Printf("Request Headers: %s\n", sanitizedHeaders) 100 fmt.Printf("Response Headers: %+v\n", res.Header()) 101 } 102 103 return nil 104 } 105 106 func mergeOptions(opts ...models.RequestOption) *models.RequestOptions { 107 options := &models.RequestOptions{} 108 for _, o := range opts { 109 o(options) 110 } 111 112 return options 113 }