-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrequest.go
139 lines (108 loc) · 2.67 KB
/
request.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
package rtm
import (
"bytes"
"context"
"crypto/md5"
"fmt"
"net/http"
"net/url"
"sort"
"strings"
"time"
"github.com/kawaken/go-rtm/methods"
)
const (
// APIEndpoint is the REST endpoint URL
APIEndpoint = "https://api.rememberthemilk.com/services/rest/"
// AuthenticationURL is authentication URL
AuthenticationURL = "https://www.rememberthemilk.com/services/auth/"
)
func appendRequiredParams(v url.Values, apiKey string, apiSecret string) url.Values {
v.Set("api_key", apiKey)
v.Set("format", "json")
// add sign
v.Set("api_sig", sign(v, apiSecret))
return v
}
func sign(values url.Values, apiSecret string) string {
var keys []string
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)
buf := bytes.NewBufferString(apiSecret)
for _, k := range keys {
buf.WriteString(k)
for _, v := range values[k] {
buf.WriteString(v)
}
}
return fmt.Sprintf("%x", md5.Sum(buf.Bytes()))
}
// Client is api client.
type Client struct {
apiKey string
apiSecret string
endpoint string
httpClient *http.Client
}
// ClientOption is option
type ClientOption func(*Client) error
// NewClient returns a new RTM Client.
func NewClient(apiKey string, apiSecret string, options ...ClientOption) (*Client, error) {
if apiKey == "" {
return nil, fmt.Errorf("missing apiKey")
}
if apiSecret == "" {
return nil, fmt.Errorf("missing apiSecret")
}
c := &Client{
apiKey: apiKey,
apiSecret: apiSecret,
endpoint: APIEndpoint,
httpClient: http.DefaultClient,
}
for _, o := range options {
err := o(c)
if err != nil {
return nil, err
}
}
return c, nil
}
// WithHTTPClient is used to set HTTP Client
func WithHTTPClient(c *http.Client) ClientOption {
return func(client *Client) error {
client.httpClient = c
return nil
}
}
// WithEndpoint is used to set endpoint
func WithEndpoint(endpoint string) ClientOption {
return func(client *Client) error {
client.endpoint = endpoint
return nil
}
}
func (c *Client) do(req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", "go-rtm-client")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req.WithContext(ctx)
return c.httpClient.Do(req)
}
// Do requests a RTM API method.
func (c *Client) Do(m *methods.Method) (*http.Response, error) {
if m.Params.Get("method") == "" {
return nil, fmt.Errorf("API method name is not defined")
}
v := appendRequiredParams(m.Params, c.apiKey, c.apiSecret)
req, err := http.NewRequest(
http.MethodPost, c.endpoint, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
return c.do(req)
}