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 (#124)
by Victor Hugo
01:19
created

mollie.*Client.HasAccessToken   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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