forked from luno/luno-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
luno.go
154 lines (131 loc) · 3.6 KB
/
luno.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
// Package luno is a wrapper for the Luno API.
package luno
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"runtime"
"strings"
"time"
)
// Client is a Luno API client.
type Client struct {
httpClient *http.Client
baseURL string
apiKeyID string
apiKeySecret string
debug bool
}
const defaultBaseURL = "https://api.luno.com"
const defaultTimeout = 10 * time.Second
// NewClient creates a new Luno API client with the default base URL.
func NewClient() *Client {
return &Client{
httpClient: &http.Client{Timeout: defaultTimeout},
baseURL: defaultBaseURL,
}
}
// SetAuth provides the client with an API key and secret.
func (cl *Client) SetAuth(apiKeyID, apiKeySecret string) error {
if apiKeyID == "" || apiKeySecret == "" {
return errors.New("luno: no credentials provided")
}
cl.apiKeyID = apiKeyID
cl.apiKeySecret = apiKeySecret
return nil
}
// SetHTTPClient sets the HTTP client that will be used for API calls.
func (cl *Client) SetHTTPClient(httpClient *http.Client) {
cl.httpClient = httpClient
}
// SetTimeout sets the timeout for requests made by this client. Note: if you
// set a timeout and then call .SetHTTPClient(), the timeout in the new HTTP
// client will be used.
func (cl *Client) SetTimeout(timeout time.Duration) {
cl.httpClient.Timeout = timeout
}
// SetBaseURL overrides the default base URL. For internal use.
func (cl *Client) SetBaseURL(baseURL string) {
cl.baseURL = strings.TrimRight(baseURL, "/")
}
// SetDebug enables or disables debug mode. In debug mode, HTTP requests and
// responses will be logged.
func (cl *Client) SetDebug(debug bool) {
cl.debug = debug
}
func (cl *Client) do(ctx context.Context, method, path string,
req, res interface{}, auth bool) error {
url := cl.baseURL + "/" + strings.TrimLeft(path, "/")
if cl.debug {
log.Printf("luno: Call: %s %s", method, path)
log.Printf("luno: Request: %#v", req)
}
var contentType string
var body io.Reader
if req != nil {
values := makeURLValues(req)
if strings.Contains(path, "{id}") {
url = strings.Replace(url, "{id}", values.Get("id"), -1)
values.Del("id")
}
if method == http.MethodGet {
url = url + "?" + values.Encode()
} else {
body = strings.NewReader(values.Encode())
contentType = "application/x-www-form-urlencoded"
}
}
httpReq, err := http.NewRequest(method, url, body)
if err != nil {
return err
}
httpReq = httpReq.WithContext(ctx)
httpReq.Header.Set("User-Agent", makeUserAgent())
if contentType != "" {
httpReq.Header.Set("Content-Type", contentType)
}
if auth {
httpReq.SetBasicAuth(cl.apiKeyID, cl.apiKeySecret)
}
if method != http.MethodGet {
httpReq.Header.Set("content-type", "application/x-www-form-urlencoded")
}
httpRes, err := cl.httpClient.Do(httpReq)
if err != nil {
return err
}
defer httpRes.Body.Close()
body = httpRes.Body
if cl.debug {
b, err := ioutil.ReadAll(body)
if err != nil {
log.Printf("luno: Error reading response body: %v", err)
} else {
log.Printf("Response: %s", string(b))
}
body = bytes.NewReader(b)
}
if httpRes.StatusCode == http.StatusTooManyRequests {
return errors.New("luno: too many requests")
}
if httpRes.StatusCode != http.StatusOK {
var e Error
err := json.NewDecoder(body).Decode(&e)
if err != nil {
return fmt.Errorf("luno: error decoding response (%d %s)",
httpRes.StatusCode, http.StatusText(httpRes.StatusCode))
}
return e
}
return json.NewDecoder(body).Decode(res)
}
func makeUserAgent() string {
return fmt.Sprintf("LunoGoSDK/%s %s %s %s",
Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
}