github.com/supabase/cli@v1.168.1/pkg/fetcher/http.go (about)

     1  package fetcher
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io"
     8  	"net/http"
     9  
    10  	"github.com/go-errors/errors"
    11  )
    12  
    13  type Fetcher struct {
    14  	server  string
    15  	client  *http.Client
    16  	editors []RequestEditor
    17  }
    18  
    19  type FetcherOption func(*Fetcher)
    20  
    21  func NewFetcher(server string, opts ...FetcherOption) *Fetcher {
    22  	api := &Fetcher{
    23  		server: server,
    24  		client: http.DefaultClient,
    25  	}
    26  	for _, apply := range opts {
    27  		apply(api)
    28  	}
    29  	return api
    30  }
    31  
    32  func WithHTTPClient(client *http.Client) FetcherOption {
    33  	return func(s *Fetcher) {
    34  		s.client = client
    35  	}
    36  }
    37  
    38  func WithBearerToken(token string) FetcherOption {
    39  	addHeader := func(req *http.Request) {
    40  		req.Header.Add("Authorization", "Bearer "+token)
    41  	}
    42  	return WithRequestEditor(addHeader)
    43  }
    44  
    45  func WithUserAgent(agent string) FetcherOption {
    46  	addHeader := func(req *http.Request) {
    47  		req.Header.Add("User-Agent", agent)
    48  	}
    49  	return WithRequestEditor(addHeader)
    50  }
    51  
    52  func WithRequestEditor(fn RequestEditor) FetcherOption {
    53  	return func(s *Fetcher) {
    54  		s.editors = append(s.editors, fn)
    55  	}
    56  }
    57  
    58  type RequestEditor func(req *http.Request)
    59  
    60  func (s *Fetcher) Send(ctx context.Context, method, path string, reqBody any, reqEditors ...RequestEditor) (*http.Response, error) {
    61  	body, ok := reqBody.(io.Reader)
    62  	if !ok && reqBody != nil {
    63  		var buf bytes.Buffer
    64  		enc := json.NewEncoder(&buf)
    65  		if err := enc.Encode(reqBody); err != nil {
    66  			return nil, errors.Errorf("failed to encode request body: %w", err)
    67  		}
    68  		reqEditors = append(reqEditors, func(req *http.Request) {
    69  			req.Header.Set("Content-Type", "application/json")
    70  		})
    71  		body = &buf
    72  	}
    73  	// Creates request
    74  	req, err := http.NewRequestWithContext(ctx, method, s.server+path, body)
    75  	if err != nil {
    76  		return nil, errors.Errorf("failed to initialise http request: %w", err)
    77  	}
    78  	for _, apply := range s.editors {
    79  		apply(req)
    80  	}
    81  	for _, apply := range reqEditors {
    82  		apply(req)
    83  	}
    84  	// Sends request
    85  	resp, err := s.client.Do(req)
    86  	if err != nil {
    87  		return nil, errors.Errorf("failed to execute http request: %w", err)
    88  	}
    89  	if resp.StatusCode >= http.StatusBadRequest {
    90  		defer resp.Body.Close()
    91  		data, err := io.ReadAll(resp.Body)
    92  		if err != nil {
    93  			return nil, errors.Errorf("Error status %d: %w", resp.StatusCode, err)
    94  		}
    95  		return nil, errors.Errorf("Error status %d: %s", resp.StatusCode, data)
    96  	}
    97  	return resp, nil
    98  }
    99  
   100  func ParseJSON[T any](r io.Reader) (*T, error) {
   101  	var data T
   102  	dec := json.NewDecoder(r)
   103  	if err := dec.Decode(&data); err != nil {
   104  		return nil, errors.Errorf("failed to parse response body: %w", err)
   105  	}
   106  	return &data, nil
   107  }