diff options
Diffstat (limited to 'internal/client/client.go')
| -rw-r--r-- | internal/client/client.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..098479f --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,119 @@ +package client + +import ( + "bytes" + "dborg/internal/config" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +type Client struct { + config *config.Config + httpClient *http.Client +} + +func New(cfg *config.Config) (*Client, error) { + if err := cfg.Validate(); err != nil { + return nil, err + } + + return &Client{ + config: cfg, + httpClient: &http.Client{ + Timeout: cfg.Timeout, + }, + }, nil +} + +func (c *Client) doRequest(method, path string, params url.Values, body interface{}) ([]byte, error) { + fullURL := c.config.BaseURL + path + if params != nil && len(params) > 0 { + fullURL += "?" + params.Encode() + } + + var reqBody io.Reader + if body != nil { + jsonData, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("failed to marshal request body: %w", err) + } + reqBody = bytes.NewBuffer(jsonData) + } + + req, err := http.NewRequest(method, fullURL, reqBody) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("X-API-Key", c.config.APIKey) + req.Header.Set("User-Agent", c.config.UserAgent) + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + + var resp *http.Response + var lastErr error + + for attempt := 0; attempt <= c.config.MaxRetries; attempt++ { + if attempt > 0 { + time.Sleep(time.Duration(attempt) * time.Second) + } + + resp, err = c.httpClient.Do(req) + if err != nil { + lastErr = err + continue + } + + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated { + return io.ReadAll(resp.Body) + } + + bodyBytes, _ := io.ReadAll(resp.Body) + + switch resp.StatusCode { + case http.StatusForbidden: + lastErr = fmt.Errorf("access denied (403): %s - This endpoint requires premium access", string(bodyBytes)) + case http.StatusUnauthorized: + lastErr = fmt.Errorf("unauthorized (401): %s - Check your API key", string(bodyBytes)) + case http.StatusTooManyRequests: + lastErr = fmt.Errorf("rate limit exceeded (429): %s", string(bodyBytes)) + case http.StatusBadRequest: + lastErr = fmt.Errorf("bad request (400): %s", string(bodyBytes)) + default: + lastErr = fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(bodyBytes)) + } + + if resp.StatusCode != http.StatusTooManyRequests && resp.StatusCode < 500 { + break + } + } + + return nil, lastErr +} + +func (c *Client) Get(path string, params url.Values) ([]byte, error) { + return c.doRequest(http.MethodGet, path, params, nil) +} + +func (c *Client) Post(path string, body interface{}) ([]byte, error) { + return c.doRequest(http.MethodPost, path, nil, body) +} + +func (c *Client) Delete(path string) ([]byte, error) { + return c.doRequest(http.MethodDelete, path, nil, nil) +} + +func (c *Client) Patch(path string, body interface{}) ([]byte, error) { + return c.doRequest(http.MethodPatch, path, nil, body) +} + +func (c *Client) Put(path string, body interface{}) ([]byte, error) { + return c.doRequest(http.MethodPut, path, nil, body) +} |
