-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclient.go
147 lines (125 loc) · 3.35 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
package conekta
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"time"
)
// Conekta API main endpoint
const baseUrl = "https://api.conekta.io/"
// Available configuration options, if not provided sane values will be
// used by default
type Options struct {
// Time to wait for requests, in seconds
Timeout uint
// Time to maintain open the connection with the service, in seconds
KeepAlive uint
// Maximum network connections to keep open with the service
MaxConnections uint
// API version to use
APIVersion string
// User agent value to report to the service
UserAgent string
}
// Main service handler
type Client struct {
// Methods related to 'orders' management
Orders OrdersAPI
// Methods related to 'customers' management
Customers CustomersAPI
// Methods related to 'plans' management
Plans PlansAPI
c *http.Client
key string
apiVersion string
userAgent string
}
// Network request options
type requestOptions struct {
method string
endpoint string
data interface{}
}
// Return sane default configuration values
func defaultOptions() *Options {
return &Options{
Timeout: 30,
KeepAlive: 600,
MaxConnections: 100,
APIVersion: "v2.0.0",
UserAgent: "",
}
}
// NewClient will construct a usable service handler using the provided API key and
// configuration options, if 'nil' options are provided default sane values will
// be used
func NewClient(key string, options *Options) (*Client, error) {
if key == "" {
return nil, errors.New("API key is required")
}
// If no options are provided, use default sane values
if options == nil {
options = defaultOptions()
}
// Configure base HTTP transport
t := &http.Transport{
MaxIdleConns: int(options.MaxConnections),
MaxIdleConnsPerHost: int(options.MaxConnections),
DialContext: (&net.Dialer{
Timeout: time.Duration(options.Timeout) * time.Second,
KeepAlive: time.Duration(options.KeepAlive) * time.Second,
DualStack: true,
}).DialContext,
}
// Setup main client
client := &Client{
key: key,
apiVersion: options.APIVersion,
userAgent: options.UserAgent,
c: &http.Client{
Transport: t,
Timeout: time.Duration(options.Timeout) * time.Second,
},
}
client.Orders = &ordersClient{c: client}
client.Customers = &customersClient{c: client}
client.Plans = &plansClient{c: client}
return client, nil
}
// Dispatch a network request to the service
func (i *Client) request(r *requestOptions) ([]byte, error) {
// Build request with headers and credentials
data, _ := json.Marshal(r.data)
req, _ := http.NewRequest(r.method, r.endpoint, bytes.NewReader(data))
req.Header.Add("Accept", fmt.Sprintf("application/vnd.conekta-%s+json", i.apiVersion))
req.Header.Add("Content-Type", "application/json")
req.SetBasicAuth(i.key, "")
if i.userAgent != "" {
req.Header.Add("User-Agent", i.userAgent)
}
// Execute request
res, err := i.c.Do(req)
if res != nil {
// Properly discard request content to be able to reuse the connection
defer io.Copy(ioutil.Discard, res.Body)
defer res.Body.Close()
}
// Network level errors
if err != nil {
return nil, err
}
// Get response contents
body, err := ioutil.ReadAll(res.Body)
// Application level errors
if res.StatusCode != 200 {
e := &APIError{}
json.Unmarshal(body, e)
return nil, e
}
return body, nil
}