/
client.go
191 lines (169 loc) · 5.61 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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Package client implements a client to connect with CryptoMarket using
// the endpoints given at https://developers.cryptomkt.com/.
package conn
import (
"bytes"
"fmt"
"github.com/cryptomkt/cryptomkt-go/args"
"github.com/cryptomkt/cryptomkt-go/requests"
"io/ioutil"
"net/http"
"net/url"
"path"
"sort"
"strings"
)
var (
// DELAY is the amount to wait in seconds between requests to the server,
// too many requests and the ip is blocked.
DELAY float64 = 2.5
// Version of the api used to connect with crypto market.
apiVersion = "v1"
// URI to connect with crypto market api.
baseApiUri = "https://api.cryptomkt.com/"
)
// Client keep the needed data to connect with the asociated CryptoMarket account.
type Client struct {
auth *HMACAuth
httpClient *http.Client
}
func (client *Client) String() string {
return fmt.Sprintf("%#v", client)
}
// New builds a new client and returns a pointer to it.
func NewClient(apiKey, apiSecret string) *Client {
client := &Client{
auth: newAuth(apiKey, apiSecret),
httpClient: &http.Client{},
}
return client
}
// runRequest makes the builded http request to cryptoMarket,
// and read the response
func (client *Client) runRequest(httpReq *http.Request) ([]byte, error) {
resp, err := client.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("Error making request: %v", err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Error reading response: %v", err)
}
return respBody, nil
}
// getPublic makes an http request to a given enpoint, given a custom request that contains
// the needed arguments
func (client *Client) getPublic(endpoint string, request *requests.Request) ([]byte, error) {
args := request.GetArguments()
u, err := url.Parse(baseApiUri)
if err != nil {
return nil, fmt.Errorf("Error parsing url %s: %v", baseApiUri, err)
}
u.Path = path.Join(u.Path, apiVersion, endpoint)
httpReq, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, fmt.Errorf("Error building NewRequest struct: %v", err)
}
if len(args) != 0 {
q := httpReq.URL.Query()
for k, v := range args {
q.Add(k, v)
}
httpReq.URL.RawQuery = q.Encode()
}
return client.runRequest(httpReq)
}
// get comunicates to Cryptomarket via the http get method, also set
// the needed headers of the request for an authenticated communication.
func (client *Client) get(endpoint string, request *requests.Request) ([]byte, error) {
args := request.GetArguments()
u, err := url.Parse(baseApiUri)
if err != nil {
return nil, fmt.Errorf("Error parsing url %s: %v", baseApiUri, err)
}
u.Path = path.Join(u.Path, apiVersion, endpoint)
httpReq, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, fmt.Errorf("Error building NewRequest struct: %v", err)
}
// query the Arguments in the http request, if there are Arguments
if len(args) != 0 {
q := httpReq.URL.Query()
for k, v := range args {
q.Add(k, v)
}
httpReq.URL.RawQuery = q.Encode()
}
requestPath := "/" + apiVersion + "/" + endpoint
client.auth.setHeaders(httpReq, requestPath, "")
return client.runRequest(httpReq)
}
// post comunicates to Cryptomarket via the http post method, also set
// the needed headers of the request for an authenticated communication.
func (client *Client) post(endpoint string, request *requests.Request) ([]byte, error) {
args := request.GetArguments()
u, err := url.Parse(baseApiUri)
if err != nil {
return nil, fmt.Errorf("Error parsing url %s: %v", baseApiUri, err)
}
u.Path = path.Join(u.Path, apiVersion, endpoint)
// builds a form from the Arguments
form := url.Values{}
for k, v := range args {
form.Add(k, v)
}
httpReq, err := http.NewRequest("POST", u.String(), strings.NewReader(form.Encode()))
if err != nil {
return nil, fmt.Errorf("Error building NewRequest struct: %v", err)
}
//sets the body for the header, arguments must be sorted
keys := make([]string, 0, len(args))
for k := range args {
keys = append(keys, k)
}
sort.Strings(keys)
var bb bytes.Buffer
for _, k := range keys {
bb.WriteString(args[k])
}
requestPath := "/" + apiVersion + "/" + endpoint
client.auth.setHeaders(httpReq, requestPath, bb.String())
//required header for the reciever to interpret the request as a http form post
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
return client.runRequest(httpReq)
}
// makeReq builds a request to ensure the presence of required arguments, stores the
// arguments in its string form.
func makeReq(required []string, args ...args.Argument) (*requests.Request, error) {
req := requests.NewReq(required)
for _, argument := range args {
err := argument(req)
if err != nil {
return nil, fmt.Errorf("argument error: %s", err)
}
}
err := req.AssertRequired()
if err != nil {
return nil, fmt.Errorf("required arguments not meeted:%s", err)
}
return req, nil
}
// postReq builds a post request and send it to CryptoMarket.
// Returns a []byte with the response
func (client *Client) postReq(endpoint string, caller string, required []string, args ...args.Argument) ([]byte, error) {
req, err := makeReq(required, args...)
if err != nil {
return nil, fmt.Errorf("Error in %s: %s", caller, err)
}
return client.post(endpoint, req)
}
// postReq builds a getReq request and send it to CryptoMarket.
// Returns a []byte with the response
func (client *Client) getReq(endpoint string, caller string, required []string, args ...args.Argument) ([]byte, error) {
req, err := makeReq(required, args...)
if err != nil {
return nil, fmt.Errorf("Error in %s: %s", caller, err)
}
return client.get(endpoint, req)
}