-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathclient.go
144 lines (129 loc) · 3.62 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Code generated for API Clients. DO NOT EDIT.
package ngrok
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"runtime"
)
const (
apiVersion = "2"
)
var (
defaultUserAgent = "ngrok-api-go/" + _version + "/" + runtime.Version()
)
// BaseClient is a generic client for the ngrok API capable of sending
// arbitrary requests.
type BaseClient struct {
cfg *ClientConfig
}
// NewBaseClient constructs a new [BaseClient].
func NewBaseClient(cfg *ClientConfig) *BaseClient {
return &BaseClient{cfg: cfg}
}
// Do sends a request to the ngrok API.
//
// The `reqBody` and `respBody` parameters will be automatically serialized
// and deserialized.
//
// The `reqURL` may include only the `Path` component. The full URL will be
// built from the `BaseURL` in the [ClientConfig].
func (c *BaseClient) Do(ctx context.Context, method string, reqURL *url.URL, reqBody interface{}, respBody interface{}) error {
req, err := c.buildRequest(ctx, method, reqURL, reqBody)
if err != nil {
return err
}
resp, err := c.cfg.HTTPClient.Do(req)
if err != nil {
return err
}
if err = c.readResponse(resp, respBody); err != nil {
return err
}
return nil
}
func (c *BaseClient) buildRequest(ctx context.Context, method string, reqURL *url.URL, reqBody interface{}) (*http.Request, error) {
body, err := c.buildRequestBody(reqBody)
if err != nil {
return nil, err
}
reqURLString := c.cfg.BaseURL.ResolveReference(reqURL).String()
r, err := http.NewRequestWithContext(ctx, method, reqURLString, body)
if err != nil {
return nil, err
}
r.Header.Set("authorization", fmt.Sprintf("Bearer %s", c.cfg.APIKey))
r.Header.Set("user-agent", c.userAgent())
r.Header.Set("ngrok-version", apiVersion)
if body != nil {
r.Header.Set("content-type", "application/json")
}
return r, nil
}
func (c *BaseClient) buildRequestBody(reqBody interface{}) (io.Reader, error) {
if reqBody == nil {
return nil, nil
}
jsonBytes, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
return bytes.NewReader(jsonBytes), nil
}
func (c *BaseClient) readResponse(resp *http.Response, out interface{}) error {
if resp.Body != nil {
defer resp.Body.Close()
}
if resp.StatusCode >= http.StatusBadRequest {
return c.readErrorResponse(resp)
}
return c.readResponseBody(resp, out)
}
// read an error response body
func (c *BaseClient) readErrorResponse(resp *http.Response) error {
var out Error
err := c.readResponseBody(resp, &out)
if err != nil {
return err
}
return &out
}
// unmarshal a response body
func (c *BaseClient) readResponseBody(resp *http.Response, out interface{}) error {
if out == nil {
return nil
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return c.buildUnmarshalError(resp, bodyBytes, err)
}
if err := json.Unmarshal(bodyBytes, out); err != nil {
return c.buildUnmarshalError(resp, bodyBytes, err)
}
return nil
}
// if an error occurs while trying to read a response body, construct a new
// error explaining the unmarshalling failure
func (c *BaseClient) buildUnmarshalError(resp *http.Response, bodyBytes []byte, err error) error {
return &Error{
Msg: fmt.Sprintf("failed to unmarshal response body: %s. body: %s", err, bodyBytes),
StatusCode: int32(resp.StatusCode),
Details: map[string]string{
"unmarshal_error": err.Error(),
"invalid_body": string(bodyBytes),
"operation_id": resp.Header.Get("ngrok-operation-id"),
},
}
}
// Returns a user agent override if one was set on the client config. Otherwise,
// returns the default user agent.
func (c *BaseClient) userAgent() string {
if c.cfg.UserAgent != nil {
return *c.cfg.UserAgent
}
return defaultUserAgent
}