GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#159)
by Victor Hugo
01:33
created

mollie.NewClient   B

Complexity

Conditions 3

Size

Total Lines 48
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 37
dl 0
loc 48
c 0
b 0
f 0
ccs 29
cts 29
cp 1
crap 3
rs 8.9919
nop 2
1
package mollie
2
3
import (
4
	"bytes"
5
	"encoding/json"
6
	"errors"
7
	"fmt"
8
	"io"
9
	"io/ioutil"
10
	"net/http"
11
	"net/url"
12
	"os"
13
	"regexp"
14
	"runtime"
15
	"strings"
16
)
17
18
// Constants holding values for client initialization and request instantiation.
19
const (
20
	BaseURL            string = "https://api.mollie.com/"
21
	AuthHeader         string = "Authorization"
22
	TokenType          string = "Bearer"
23
	APITokenEnv        string = "MOLLIE_API_TOKEN"
24
	OrgTokenEnv        string = "MOLLIE_ORG_TOKEN"
25
	RequestContentType string = "application/json"
26
)
27
28
var (
29
	accessTokenExpr = regexp.MustCompile(`(?m)^access_`)
30
	errEmptyAuthKey = errors.New("you must provide a non-empty authentication key")
31
	errBadBaseURL   = errors.New("malformed base url, it must contain a trailing slash")
32
)
33
34
// Client manages communication with Mollie's API.
35
type Client struct {
36
	BaseURL        *url.URL
37
	authentication string
38
	userAgent      string
39
	client         *http.Client
40
	common         service // Reuse a single struct instead of allocating one for each service on the heap.
41
	config         *Config
42
	// Services
43
	Payments      *PaymentsService
44
	Chargebacks   *ChargebacksService
45
	Methods       *MethodsService
46
	Invoices      *InvoicesService
47
	Organizations *OrganizationsService
48
	Profiles      *ProfilesService
49
	Refunds       *RefundsService
50
	Shipments     *ShipmentsService
51
	Orders        *OrdersService
52
	Settlements   *SettlementsService
53
	Captures      *CapturesService
54
	Subscriptions *SubscriptionsService
55
	Customers     *CustomersService
56
	Miscellaneous *MiscellaneousService
57
	Mandates      *MandatesService
58
	Permissions   *PermissionsService
59
	Onboarding    *OnboardingService
60
	PaymentLinks  *PaymentLinksService
61
	Partners      *PartnerService
62
}
63
64
type service struct {
65
	client *Client
66
}
67
68
// WithAuthenticationValue offers a convenient setter for any of the valid authentication
69
// tokens provided by Mollie.
70
//
71
// Ideally your API key will be provided from and environment variable or
72
// a secret management engine.
73
// This should only be used when environment variables are "impossible" to be used.
74
func (c *Client) WithAuthenticationValue(k string) error {
75 1
	if k == "" {
76 1
		return errEmptyAuthKey
77
	}
78
79 1
	c.authentication = strings.TrimSpace(k)
80
81 1
	return nil
82
}
83
84
// HasAccessToken will return true when the provided authentication token
85
// complies with the access token REGEXP match check.
86
// This will enable TestMode inside the request body.
87
//
88
// See: https://github.com/VictorAvelar/mollie-api-go/issues/123
89
func (c *Client) HasAccessToken() bool {
90 1
	return accessTokenExpr.Match([]byte(c.authentication))
91
}
92
93
// NewAPIRequest is a wrapper around the http.NewRequest function.
94
//
95
// It will setup the authentication headers/parameters according to the client config.
96
func (c *Client) NewAPIRequest(method string, uri string, body interface{}) (req *http.Request, err error) {
97 1
	if !strings.HasSuffix(c.BaseURL.Path, "/") {
98 1
		return nil, errBadBaseURL
99
	}
100
101 1
	u, err := c.BaseURL.Parse(uri)
102 1
	if err != nil {
103 1
		return nil, err
104
	}
105
106 1
	if c.config.testing {
107 1
		u.Query().Add("testmode", "true")
108
	}
109
110 1
	var buf io.ReadWriter
111 1
	if body != nil {
112 1
		buf = new(bytes.Buffer)
113 1
		enc := json.NewEncoder(buf)
114 1
		enc.SetEscapeHTML(false)
115 1
		err := enc.Encode(body)
116 1
		if err != nil {
117 1
			return nil, err
118
		}
119
	}
120
121 1
	req, err = http.NewRequest(method, u.String(), buf)
122 1
	if err != nil {
123 1
		return nil, err
124
	}
125
126 1
	req.Header.Add(AuthHeader, strings.Join([]string{TokenType, c.authentication}, " "))
127 1
	req.Header.Set("Content-Type", RequestContentType)
128 1
	req.Header.Set("Accept", RequestContentType)
129 1
	req.Header.Set("User-Agent", c.userAgent)
130
131 1
	return
132
}
133
134
// Do sends an API request and returns the API response or returned as an
135
// error if an API error has occurred.
136
func (c *Client) Do(req *http.Request) (*Response, error) {
137 1
	resp, err := c.client.Do(req)
138 1
	if err != nil {
139 1
		return nil, err
140
	}
141 1
	defer resp.Body.Close()
142 1
	response, _ := newResponse(resp)
143 1
	err = CheckResponse(resp)
144 1
	if err != nil {
145 1
		return response, err
146
	}
147
148 1
	return response, nil
149
}
150
151
// NewClient returns a new Mollie HTTP API client.
152
// You can pass a previously build http client, if none is provided then
153
// http.DefaultClient will be used.
154
//
155
// NewClient will lookup the environment for values to assign to the
156
// API token (`MOLLIE_API_TOKEN`) and the Organization token (`MOLLIE_ORG_TOKEN`)
157
// according to the provided Config object.
158
//
159
// You can also set the token values programmatically by using the Client
160
// WithAPIKey and WithOrganizationKey functions.
161
func NewClient(baseClient *http.Client, c *Config) (mollie *Client, err error) {
162 1
	if baseClient == nil {
163 1
		baseClient = http.DefaultClient
164
	}
165
166 1
	u, _ := url.Parse(BaseURL)
167
168 1
	mollie = &Client{
169
		BaseURL: u,
170
		client:  baseClient,
171
		config:  c,
172
	}
173
174 1
	mollie.common.client = mollie
175
176
	// services for resources
177 1
	mollie.Payments = (*PaymentsService)(&mollie.common)
178 1
	mollie.Chargebacks = (*ChargebacksService)(&mollie.common)
179 1
	mollie.Methods = (*MethodsService)(&mollie.common)
180 1
	mollie.Invoices = (*InvoicesService)(&mollie.common)
181 1
	mollie.Organizations = (*OrganizationsService)(&mollie.common)
182 1
	mollie.Profiles = (*ProfilesService)(&mollie.common)
183 1
	mollie.Refunds = (*RefundsService)(&mollie.common)
184 1
	mollie.Shipments = (*ShipmentsService)(&mollie.common)
185 1
	mollie.Orders = (*OrdersService)(&mollie.common)
186 1
	mollie.Captures = (*CapturesService)(&mollie.common)
187 1
	mollie.Settlements = (*SettlementsService)(&mollie.common)
188 1
	mollie.Subscriptions = (*SubscriptionsService)(&mollie.common)
189 1
	mollie.Customers = (*CustomersService)(&mollie.common)
190 1
	mollie.Miscellaneous = (*MiscellaneousService)(&mollie.common)
191 1
	mollie.Mandates = (*MandatesService)(&mollie.common)
192 1
	mollie.Permissions = (*PermissionsService)(&mollie.common)
193 1
	mollie.Onboarding = (*OnboardingService)(&mollie.common)
194 1
	mollie.PaymentLinks = (*PaymentLinksService)(&mollie.common)
195 1
	mollie.Partners = (*PartnerService)(&mollie.common)
196
197 1
	mollie.userAgent = strings.Join([]string{
198
		runtime.GOOS,
199
		runtime.GOARCH,
200
		runtime.Version(),
201
	}, ";")
202
203
	// Parse authorization from specified environment variable
204 1
	tkn, ok := os.LookupEnv(c.auth)
205 1
	if ok {
206 1
		mollie.authentication = tkn
207
	}
208 1
	return
209
}
210
211
/*
212
Error reports details on a failed API request.
213
*/
214
type Error struct {
215
	Code     int            `json:"code"`
216
	Message  string         `json:"message"`
217
	Content  string         `json:"content,omitempty"`
218
	Response *http.Response `json:"response"` // the full response that produced the error
219
}
220
221
// Error function complies with the error interface
222
func (e *Error) Error() string {
223 1
	return fmt.Sprintf("response failed with status %s\npayload: %v", e.Message, e.Content)
224
}
225
226
/*
227
Constructor for Error
228
*/
229
func newError(r *http.Response) *Error {
230 1
	var e Error
231 1
	e.Response = r
232 1
	e.Code = r.StatusCode
233 1
	e.Message = r.Status
234 1
	c, err := ioutil.ReadAll(r.Body)
235 1
	if err == nil {
236 1
		e.Content = string(c)
237
	}
238 1
	r.Body = ioutil.NopCloser(bytes.NewBuffer(c))
239 1
	return &e
240
}
241
242
// Response is a Mollie API response. This wraps the standard http.Response
243
// returned from Mollie and provides convenient access to things like
244
// pagination links.
245
type Response struct {
246
	*http.Response
247
	content []byte
248
}
249
250
func newResponse(r *http.Response) (*Response, error) {
251 1
	var res Response
252 1
	c, err := ioutil.ReadAll(r.Body)
253 1
	if err == nil {
254 1
		res.content = c
255
	}
256 1
	err = json.NewDecoder(r.Body).Decode(&res)
257 1
	r.Body = ioutil.NopCloser(bytes.NewBuffer(c))
258 1
	res.Response = r
259 1
	return &res, err
260
}
261
262
// CheckResponse checks the API response for errors, and returns them if
263
// present. A response is considered an error if it has a status code outside
264
// the 200 range.
265
// API error responses are expected to have either no response
266
// body, or a JSON response body.
267
func CheckResponse(r *http.Response) error {
268 1
	if r.StatusCode >= http.StatusMultipleChoices {
269 1
		return newError(r)
270
	}
271 1
	return nil
272
}
273