Files
airtable/http.go
2024-06-22 21:46:58 -07:00

126 lines
2.6 KiB
Go

package airtable
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
)
func get[T any](ctx context.Context, c *Client, path string, params url.Values) (*T, error) {
return do[T](ctx, c, "GET", path, params, nil)
}
func post[T any](ctx context.Context, c *Client, path string, data any) (*T, error) {
return do[T](ctx, c, "POST", path, nil, data)
}
func del[T any](ctx context.Context, c *Client, path string, params url.Values) (*T, error) {
return do[T](ctx, c, "DELETE", path, params, nil)
}
func patch[T any](ctx context.Context, c *Client, path string, data any) (*T, error) {
return do[T](ctx, c, "PATCH", path, nil, data)
}
func put[T any](ctx context.Context, c *Client, path string, data any) (*T, error) {
return do[T](ctx, c, "PUT", path, nil, data)
}
func do[T any](ctx context.Context, c *Client, method, path string, params url.Values, data any) (*T, error) {
var err error
c.waitForRateLimit()
url := fmt.Sprintf("%s/%s", c.BaseURL, path)
body := []byte{}
if data != nil {
body, err = json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("marshalling message body: %w", err)
}
}
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("cannot create request: %w", err)
}
req.URL.RawQuery = params.Encode()
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.apiKey))
resp, err := c.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("http request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("http error: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
}
dec := json.NewDecoder(resp.Body)
target := new(T)
err = dec.Decode(target)
if err != nil {
return nil, fmt.Errorf("json decode failed: %w", err)
}
return target, nil
}
func listAll[T any](ctx context.Context, c *Client, path string, params url.Values, key string, cb func(*T) error) ([]*T, error) {
ret := []*T{}
if params == nil {
params = url.Values{}
}
for {
resp, err := get[map[string]any](ctx, c, path, params)
if err != nil {
return nil, err
}
subresp, err := json.Marshal((*resp)[key])
if err != nil {
return nil, err
}
objs := []*T{}
err = json.Unmarshal(subresp, &objs)
if err != nil {
return nil, err
}
if cb != nil {
for _, obj := range objs {
err = cb(obj)
if err != nil {
return nil, err
}
}
}
ret = append(ret, objs...)
off, found := (*resp)["offset"]
if !found {
return ret, nil
}
params.Set("offset", off.(string))
}
}