-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathrandomorg.go
More file actions
207 lines (174 loc) · 5.06 KB
/
randomorg.go
File metadata and controls
207 lines (174 loc) · 5.06 KB
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* Copyright 2015 Sören Gade
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package randomorg is a Random.org API client as described at https://api.random.org/json-rpc/2.
// This is a third-party client. See https://github.com/sgade/randomorg.
// For any method documentation you should take a look at the official API documentation.
// An API key can be acquired here: https://api.random.org/dashboard.
package randomorg
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
"github.com/pborman/uuid"
)
// Private constants
const (
// The Random.org API request endpoint URL
requestEndpoint = "https://api.random.org/json-rpc/2/invoke"
// Example time format for ISO 8601
iso8601Example = time.RFC3339Nano //"2013-02-20 17:53:40Z"
// API Error template string
errAPI = "API Error Code %v: %q."
)
// Constants describing error situations.
var (
// ErrAPIKey is the error returned when an invalid API key was given.
ErrAPIKey = errors.New("provide an api key")
// ErrJSONFormat is the error returned when the response JSON had an unexpected format.
ErrJSONFormat = errors.New("could not get key from given json")
// ErrParamRange is returned when invalid parameter ranges where given to a method.
// See the method API documentation for further details.
ErrParamRange = errors.New("invalid parameter range")
)
// A Random defines a Random.org API Client.
// For more information, see https://api.random.org/json-rpc/2.
type Random struct {
// the api key
apiKey string
// reusable http.Client
client *http.Client
// usage cache
usage *Usage
}
// NewRandom creates a new Random client with the given apiKey.
func NewRandom(apiKey string) *Random {
// check the api key
if apiKey == "" {
panic(ErrAPIKey)
}
random := Random{
apiKey: apiKey,
client: &http.Client{},
}
return &random
}
// SetProxy sets the proxy for requests indicated by the url.
func (r *Random) SetProxy(proxyURL *url.URL) error {
t := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
r.client = &http.Client{
Transport: t,
}
return nil
}
// SetProxyAddress sets the proxy for requets indicated by the url/address string.
func (r *Random) SetProxyAddress(proxyAddress string) error {
var url *url.URL
if proxyAddress != "" {
var err error
url, err = url.Parse(proxyAddress)
if err != nil {
return err
}
}
return r.SetProxy(url)
}
// Get the json object with the given key from the given json object.
func (r *Random) jsonMap(json map[string]interface{}, key string) (map[string]interface{}, error) {
value := json[key]
if value == nil {
return nil, ErrJSONFormat
}
newMap, ok := value.(map[string]interface{})
if !ok {
return nil, ErrJSONFormat
}
return newMap, nil
}
func (r *Random) invokeRequest(method string, params map[string]interface{}) (map[string]interface{}, error) {
// always append api key
params["apiKey"] = r.apiKey
// generate request UUID
requestUUID := uuid.NewUUID().String()
// build request body
requestBody := map[string]interface{}{
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": requestUUID,
}
requestBodyJSON, err := json.Marshal(requestBody)
if err != nil {
return nil, err
}
requestBodyReader := bytes.NewReader(requestBodyJSON)
req, err := http.NewRequest("POST", requestEndpoint, requestBodyReader)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
responseBody := make(map[string]interface{})
err = json.Unmarshal(body, &responseBody)
if err != nil {
if len(body) > 0 {
err = errors.New(string(body))
}
return nil, err
}
result, err := r.jsonMap(responseBody, "result")
if err != nil {
error, err := r.jsonMap(responseBody, "error")
if err != nil {
return nil, err
}
errorCode, _ := error["code"]
errorMessage, _ := error["message"]
err = fmt.Errorf(errAPI, errorCode, errorMessage)
return nil, err
}
return result, nil
}
// requestCommand invokes the request and parses all information down to the requested data block.
func (r *Random) requestCommand(method string, params map[string]interface{}) ([]interface{}, error) {
result, err := r.invokeRequest(method, params)
if err != nil {
return nil, err
}
r.parseAndSaveUsage(result)
random, err := r.jsonMap(result, "random")
if err != nil {
return nil, err
}
data := random["data"].([]interface{})
return data, nil
}