http.go

v1.0.0
Doc Versions Source
1
package bitwarden
2
3
import (
4
	"bytes"
5
	"context"
6
	"encoding/json"
7
	"fmt"
8
	"io"
9
	"net/http"
10
	"net/url"
11
)
12
13
type transport struct {
14
	baseURL    string
15
	httpClient *http.Client
16
}
17
18
func newTransport(baseURL string, httpClient *http.Client) *transport {
19
	return &transport{
20
		baseURL:    baseURL,
21
		httpClient: httpClient,
22
	}
23
}
24
25
func (t *transport) get(ctx context.Context, path string, query url.Values) ([]byte, error) {
26
	return t.do(ctx, http.MethodGet, path, query, nil)
27
}
28
29
func (t *transport) post(ctx context.Context, path string, body any) ([]byte, error) {
30
	return t.doJSON(ctx, http.MethodPost, path, body)
31
}
32
33
func (t *transport) put(ctx context.Context, path string, body any) ([]byte, error) {
34
	return t.doJSON(ctx, http.MethodPut, path, body)
35
}
36
37
func (t *transport) del(ctx context.Context, path string) ([]byte, error) {
38
	return t.do(ctx, http.MethodDelete, path, nil, nil)
39
}
40
41
func (t *transport) doJSON(ctx context.Context, method, path string, body any) ([]byte, error) {
42
	var reader io.Reader
43
	if body != nil {
44
		data, err := json.Marshal(body)
45
		if err != nil {
46
			return nil, fmt.Errorf("marshal request body: %w", err)
47
		}
48
		reader = bytes.NewReader(data)
49
	}
50
	return t.do(ctx, method, path, nil, reader)
51
}
52
53
func (t *transport) do(ctx context.Context, method, path string, query url.Values, body io.Reader) ([]byte, error) {
54
	u := t.baseURL + path
55
	if len(query) > 0 {
56
		u += "?" + query.Encode()
57
	}
58
59
	req, err := http.NewRequestWithContext(ctx, method, u, body)
60
	if err != nil {
61
		return nil, fmt.Errorf("create request: %w", err)
62
	}
63
64
	if body != nil {
65
		req.Header.Set("Content-Type", "application/json")
66
	}
67
68
	resp, err := t.httpClient.Do(req)
69
	if err != nil {
70
		return nil, fmt.Errorf("execute request: %w", err)
71
	}
72
	defer resp.Body.Close()
73
74
	respBody, err := io.ReadAll(resp.Body)
75
	if err != nil {
76
		return nil, fmt.Errorf("read response: %w", err)
77
	}
78
79
	if resp.StatusCode >= 400 {
80
		var msg struct {
81
			Message string `json:"message"`
82
		}
83
		_ = json.Unmarshal(respBody, &msg)
84
		return nil, &APIError{
85
			StatusCode: resp.StatusCode,
86
			Message:    msg.Message,
87
		}
88
	}
89
90
	return respBody, nil
91
}
92
93
func decodeJSON[T any](data []byte) (T, error) {
94
	var v T
95
	if err := json.Unmarshal(data, &v); err != nil {
96
		return v, fmt.Errorf("decode response: %w", err)
97
	}
98
	return v, nil
99
}
100
101
func decodeListResponse[T any](data []byte) ([]T, error) {
102
	resp, err := decodeJSON[ListResponse[T]](data)
103
	if err != nil {
104
		return nil, err
105
	}
106
	return resp.Data.Data, nil
107
}
108
109
func decodeObjectResponse[T any](data []byte) (T, error) {
110
	resp, err := decodeJSON[ObjectResponse[T]](data)
111
	if err != nil {
112
		return resp.Data, err
113
	}
114
	return resp.Data, nil
115
}
116

Source Files