-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
client.go
165 lines (142 loc) · 4.81 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package tonicpow
import (
"encoding/json"
"errors"
"net/http"
"strconv"
"time"
"github.com/go-resty/resty/v2"
)
type (
// Client is the TonicPow client/configuration
Client struct {
httpClient *resty.Client
options *ClientOptions // Options are all the default settings / configuration
}
// ClientOptions holds all the configuration for client requests and default resources
ClientOptions struct {
apiKey string // API key
env Environment // Environment
customHeaders map[string][]string // Custom headers on outgoing requests
httpTimeout time.Duration // Default timeout in seconds for GET requests
requestTracing bool // If enabled, it will trace the request timing
retryCount int // Default retry count for HTTP requests
userAgent string // User agent for all outgoing requests
}
// StandardResponse is the standard fields returned on all responses
StandardResponse struct {
Body []byte `json:"-"` // Body of the response request
Error *Error `json:"-"` // API error response
StatusCode int `json:"-"` // Status code returned on the request
Tracing resty.TraceInfo `json:"-"` // Trace information if enabled on the request
}
)
// NewClient creates a new client for all TonicPow requests
//
// If no options are given, it will use the DefaultClientOptions()
// If there is no client is supplied, it will use a default Resty HTTP client.
func NewClient(opts ...ClientOps) (ClientInterface, error) {
defaults := defaultClientOptions()
// Create a new client
client := &Client{
options: defaults,
}
// overwrite defaults with any set by user
for _, opt := range opts {
opt(client.options)
}
// Check for API key
if client.options.apiKey == "" {
return nil, errors.New("missing an API Key")
}
// Set the Resty HTTP client
if client.httpClient == nil {
client.httpClient = resty.New()
// Set defaults (for GET requests)
client.httpClient.SetTimeout(client.options.httpTimeout)
client.httpClient.SetRetryCount(client.options.retryCount)
}
return client, nil
}
// WithCustomHTTPClient will overwrite the default client with a custom client.
func (c *Client) WithCustomHTTPClient(client *resty.Client) *Client {
c.httpClient = client
return c
}
// GetUserAgent will return the user agent string of the client
func (c *Client) GetUserAgent() string {
return c.options.userAgent
}
// GetEnvironment will return the Environment of the client
func (c *Client) GetEnvironment() Environment {
return c.options.env
}
// Options will return the clients current options
func (c *Client) Options() *ClientOptions {
return c.options
}
// Request is a standard GET / POST / PUT / DELETE request for all outgoing HTTP requests
// Omit the data attribute if using a GET request
func (c *Client) Request(httpMethod string, requestEndpoint string,
data interface{}, expectedCode int) (response *StandardResponse, err error) {
// Set the user agent
req := c.httpClient.R().SetHeader("User-Agent", c.options.userAgent)
// Set the body if (PUT || POST)
if httpMethod != http.MethodGet && httpMethod != http.MethodDelete {
var j []byte
if j, err = json.Marshal(data); err != nil {
return
}
req = req.SetBody(string(j))
req.Header.Add("Content-Length", strconv.Itoa(len(j)))
req.Header.Set("Content-Type", "application/json")
}
// Enable tracing
if c.options.requestTracing {
req.EnableTrace()
}
// Set the authorization and content type
req.Header.Set(fieldAPIKey, c.options.apiKey)
// Custom headers?
for key, headers := range c.options.customHeaders {
for _, value := range headers {
req.Header.Set(key, value)
}
}
// Fire the request
var resp *resty.Response
switch httpMethod {
case http.MethodPost:
resp, err = req.Post(c.options.env.URL() + requestEndpoint)
case http.MethodPut:
resp, err = req.Put(c.options.env.URL() + requestEndpoint)
case http.MethodDelete:
resp, err = req.Delete(c.options.env.URL() + requestEndpoint)
case http.MethodGet:
resp, err = req.Get(c.options.env.URL() + requestEndpoint)
}
if err != nil {
return
}
// Start the response
response = new(StandardResponse)
// Tracing enabled?
if c.options.requestTracing {
response.Tracing = resp.Request.TraceInfo()
}
// Set the status code & body
response.StatusCode = resp.StatusCode()
response.Body = resp.Body()
// Check expected code if set
if expectedCode > 0 && response.StatusCode != expectedCode {
response.Error = new(Error)
if err = json.Unmarshal(response.Body, &response.Error); err != nil {
return
}
err = errors.New(response.Error.Message)
if response.StatusCode == 0 { // If a 200 is expected, but you get a 201, this case occurs (improper status code check)
response.StatusCode = http.StatusInternalServerError
}
}
return
}