/
client.go
153 lines (131 loc) · 3.96 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
package shippinglabel
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"golang.org/x/net/http2"
)
const productionURL = "https://api.shippinglabel.de/v2"
const developmentURL = "https://api.dev.shippinglabel.de/v2"
type Client struct {
baseURL string
clientID string
clientSecret string
hc *http.Client
}
func NewClient(clientID string, clientSecret string) (*Client, error) {
if clientID == "" || clientSecret == "" {
return nil, ErrRequiredClientIDAndSecret
}
c := &Client{clientID: clientID, clientSecret: clientSecret}
c.Development()
c.hc = c.defaultHTTPClient()
return c, nil
}
// Production sets the productionURL as default
func (c *Client) Production() {
c.baseURL = productionURL
}
// Development sets the developmentURL as default
func (c *Client) Development() {
c.baseURL = developmentURL
}
// SetHTTPClient sets the default http.Client
func (c *Client) SetHTTPClient(hc *http.Client) {
c.hc = hc
}
// defaultHTTPClient sets the default http.Client
func (c *Client) defaultHTTPClient() *http.Client {
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
_ = http2.ConfigureTransport(t)
hc := &http.Client{
Transport: t,
Timeout: 2 * time.Minute,
}
return hc
}
// APIContext creates a token specific context for the REST API
func (c *Client) APIContext(token *AuthToken) (*APIContext, error) {
return NewAPIContext(c, token)
}
// send sends the request to the Shippinglabel REST API
func (c *Client) send(ctx context.Context, req *request) error {
httpReq, err := req.HTTPRequest(ctx)
if err != nil {
return err
}
// Send request
resp, err := c.hc.Do(httpReq)
if err != nil {
return err
}
defer resp.Body.Close()
// Check status code
if resp.StatusCode >= 400 {
em := &Error{}
if err = json.NewDecoder(resp.Body).Decode(em); err != nil {
return err
}
return em
}
// Check resp handler was set
if req.respHandler == nil {
_, err = io.ReadAll(resp.Body)
return err
}
// Handle response
return req.respHandler(resp)
}
// sendTokenRequest sends a request to receive an access token for the ClientCredentials, AuthorizationCode and RefreshToken functions
func (c *Client) sendTokenRequest(ctx context.Context, qs url.Values) (*AuthToken, error) {
var tk *AuthToken
req := newRequest(c.baseURL).SetBasicAuth(c.clientID, c.clientSecret).SetFormValues(qs).ToJSON(&tk).
SetMethod(http.MethodPost).SetPath("/oauth2/token")
if err := c.send(ctx, req); err != nil {
return nil, err
}
tk.SetExpirationTime()
return tk, nil
}
// ClientCredentials creates an access token with the client credentials
func (c *Client) ClientCredentials(ctx context.Context) (*AuthToken, error) {
qs := url.Values{}
qs.Add("grant_type", "client_credentials")
return c.sendTokenRequest(ctx, qs)
}
// AuthCodeURL creates a redirect url for the shippinglabel oauth process (AuthorizationCode)
func (c *Client) AuthCodeURL(redirectURL string, state string) string {
qs := url.Values{}
qs.Add("client_id", c.clientID)
qs.Add("redirect_uri", redirectURL)
qs.Add("response_type", "code")
if state != "" {
qs.Add("state", state)
}
return fmt.Sprintf("%s/oauth2/authorize?%s", c.baseURL, qs.Encode())
}
// AuthorizationCode exchanges the authorization code for an access token
func (c *Client) AuthorizationCode(ctx context.Context, authCode string) (resp *AuthToken, err error) {
qs := url.Values{}
qs.Add("grant_type", "authorization_code")
qs.Add("code", authCode)
return c.sendTokenRequest(ctx, qs)
}
// RefreshToken creates an access token through a refresh token
func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (resp *AuthToken, err error) {
qs := url.Values{}
qs.Add("grant_type", "refresh_token")
qs.Add("refresh_token", refreshToken)
return c.sendTokenRequest(ctx, qs)
}