-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathkraken.go
165 lines (150 loc) · 3.94 KB
/
kraken.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 kraken is the official client's implementation for Kraken.io API
//https://github.com/kraken-io/kraken-go
package kraken
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"path/filepath"
)
var (
//ErrNoCred indicates that api_key or api_secret were not specified
ErrNoCred = fmt.Errorf("key and secret should be specified")
)
//ResponseError contains error encountered while processing HTTP response
//with response itself
type ResponseError struct {
s string
Resp *http.Response
}
func (err *ResponseError) Error() string { return err.s }
//Kraken holds api_key, api_secret, and HttpClient
type Kraken struct {
auth map[string]string
//If not specified default http.Client{} is used
HTTPClient *http.Client
}
//New creates new instance of Kraken. key and secret shouldn't be empty,otherwise ErrNoCred will be returned
func New(key, secret string) (*Kraken, error) {
if key == "" || secret == "" {
return nil, ErrNoCred
}
return &Kraken{
auth: map[string]string{
"api_key": key,
"api_secret": secret,
},
HTTPClient: http.DefaultClient,
}, nil
}
// URL makes request to the https://api.kraken.io/v1/url endpoint
func (kr *Kraken) URL(params map[string]interface{}) (map[string]interface{}, error) {
dataReq, err := kr.marshalParams(params)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", "https://api.kraken.io/v1/url", bytes.NewBuffer(dataReq))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
return kr.doReq(req)
}
// Upload opens a file and makes multipart request to the https://api.kraken.io/v1/upload endpoint
func (kr *Kraken) Upload(params map[string]interface{}, path string) (map[string]interface{}, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return kr.UploadReader(params, file, filepath.Base(path))
}
// UploadReader makes multipart request to the https://api.kraken.io/v1/upload endpoint
func (kr *Kraken) UploadReader(params map[string]interface{}, f io.Reader, name string) (map[string]interface{}, error) {
pipeReader, pipeWriter := io.Pipe()
writer := multipart.NewWriter(pipeWriter)
req, err := http.NewRequest("POST", "https://api.kraken.io/v1/upload", pipeReader)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", writer.FormDataContentType())
cancelCh := make(chan struct{})
req.Cancel = cancelCh
errCh := make(chan error, 1)
go func() {
defer pipeWriter.Close()
data, err := kr.marshalParams(params)
if err != nil {
errCh <- err
close(cancelCh)
return
}
dataPart, err := writer.CreateFormField("data")
if err != nil {
errCh <- err
close(cancelCh)
return
}
_, err = dataPart.Write(data)
if err != nil {
errCh <- err
close(cancelCh)
return
}
uploadPart, err := writer.CreateFormFile("upload", name)
if err != nil {
errCh <- err
close(cancelCh)
return
}
_, err = io.Copy(uploadPart, f)
if err != nil {
errCh <- err
close(cancelCh)
return
}
err = writer.Close()
if err != nil {
errCh <- err
close(cancelCh)
}
}()
dataResp, err := kr.doReq(req)
if err != nil {
select {
case err := <-errCh:
return nil, err
default:
return nil, err
}
}
return dataResp, nil
}
func (kr *Kraken) marshalParams(params map[string]interface{}) ([]byte, error) {
params["auth"] = kr.auth
return json.Marshal(params)
}
func (kr *Kraken) doReq(req *http.Request) (map[string]interface{}, error) {
resp, err := kr.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, &ResponseError{err.Error(), resp}
}
dataResp := make(map[string]interface{})
err = json.Unmarshal(body, &dataResp)
if err != nil {
resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
return nil, &ResponseError{err.Error(), resp}
}
return dataResp, nil
}