flutterwave.*Client.newRequest   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 15
dl 0
loc 21
rs 9.65
c 0
b 0
f 0
nop 4
1
package flutterwave
2
3
import (
4
	"bytes"
5
	"context"
6
	"encoding/json"
7
	"errors"
8
	"fmt"
9
	"io"
10
	"net/http"
11
)
12
13
// Pre-defined error messages to be shared across services.
14
var (
15
	ErrCouldNotConstructNewRequest = errors.New("could not construct new request")
16
	ErrRequestFailure  = errors.New("request failed")
17
	ErrUnmarshalFailure = errors.New("failed to unmarshal response")
18
)
19
20
type service struct {
21
	client *Client
22
}
23
24
// Client is the flutterwave API client.
25
// Do not instantiate this client with Client{}. Use the New method instead.
26
type Client struct {
27
	httpClient *http.Client
28
	common     service
29
	secretKey  string
30
	baseURL    string
31
32
	Bills        *billsService
33
	Payments     *paymentsService
34
	Transactions *transactionsService
35
	Transfers *transfersService
36
}
37
38
// New creates and returns a new flutterwave.Client from a slice of flutterwave.ClientOption.
39
func New(options ...ClientOption) *Client {
40
	config := defaultClientConfig()
41
42
	for _, option := range options {
43
		option.apply(config)
44
	}
45
46
	client := &Client{
47
		httpClient: config.httpClient,
48
		secretKey:  config.secretKey,
49
		baseURL:    config.baseURL,
50
	}
51
52
	client.common.client = client
53
	client.Bills = (*billsService)(&client.common)
54
	client.Payments = (*paymentsService)(&client.common)
55
	client.Transactions = (*transactionsService)(&client.common)
56
	client.Transfers = (*transfersService)(&client.common)
57
	return client
58
}
59
60
// newRequest creates an API request. A relative URL can be provided in uri,
61
// in which case it is resolved relative to the BaseURL of the Client.
62
// URI's should always be specified without a preceding slash.
63
func (client *Client) newRequest(ctx context.Context, method, uri string, body interface{}) (*http.Request, error) {
64
	var buf io.ReadWriter
65
	if body != nil {
66
		buf = &bytes.Buffer{}
67
		enc := json.NewEncoder(buf)
68
		enc.SetEscapeHTML(false)
69
		err := enc.Encode(body)
70
		if err != nil {
71
			return nil, err
72
		}
73
	}
74
75
	req, err := http.NewRequestWithContext(ctx, method, client.baseURL+uri, buf)
76
	if err != nil {
77
		return nil, err
78
	}
79
80
	req.Header.Set("Content-Type", "application/json")
81
	req.Header.Set("Authorization", "Bearer "+client.secretKey)
82
83
	return req, nil
84
}
85
86
// addURLParams adds urls parameters to an *http.Request
87
func (client *Client) addURLParams(request *http.Request, params map[string]string) *http.Request {
88
	q := request.URL.Query()
89
	for key, value := range params {
90
		q.Add(key, value)
91
	}
92
	request.URL.RawQuery = q.Encode()
93
	return request
94
}
95
96
// do carries out an HTTP request and returns a Response
97
func (client *Client) do(req *http.Request) (*Response, error) {
98
	if req == nil {
99
		return nil, fmt.Errorf("%T cannot be nil", req)
100
	}
101
102
	httpResponse, err := client.httpClient.Do(req)
103
	if err != nil {
104
		return nil, err
105
	}
106
107
	defer func() { _ = httpResponse.Body.Close() }()
108
109
	resp, err := client.newResponse(httpResponse)
110
	if err != nil {
111
		return resp, err
112
	}
113
114
	_, err = io.Copy(io.Discard, httpResponse.Body)
115
	if err != nil {
116
		return resp, err
117
	}
118
119
	return resp, nil
120
}
121
122
// newResponse converts an *http.Response to *Response
123
func (client *Client) newResponse(httpResponse *http.Response) (*Response, error) {
124
	if httpResponse == nil {
125
		return nil, fmt.Errorf("%T cannot be nil", httpResponse)
126
	}
127
128
	resp := new(Response)
129
	resp.HTTPResponse = httpResponse
130
131
	buf, err := io.ReadAll(resp.HTTPResponse.Body)
132
	if err != nil {
133
		return nil, err
134
	}
135
	resp.Body = &buf
136
137
	return resp, resp.Error()
138
}
139