Skip to content

Commit 62ee3f1

Browse files
authored
Initial commit
0 parents  commit 62ee3f1

File tree

16 files changed

+690
-0
lines changed

16 files changed

+690
-0
lines changed

.github/workflows/main.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Build
2+
3+
'on':
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
Validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v2
14+
15+
- name: Setup Go
16+
uses: actions/setup-go@v2
17+
18+
- name: Setup Dependencies
19+
run: |
20+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $GOPATH/bin v1.24.0
21+
golangci-lint --version
22+
go get golang.org/x/tools/cmd/cover
23+
go get -t -v ./...
24+
25+
- name: Golang CI Lint
26+
run: golangci-lint run
27+
28+
Test:
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v2
33+
34+
- name: Setup Go
35+
uses: actions/setup-go@v2
36+
37+
- name: Setup Dependencies
38+
run: |
39+
go get golang.org/x/tools/cmd/cover
40+
go get -t -v ./...
41+
42+
- name: Run Tests
43+
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic
44+
45+
- name: Upload coverage to Codecov
46+
run: bash <(curl -s https://codecov.io/bash)

.gitignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# Dependency directories (remove the comment below to include it)
15+
# vendor/
16+
$path
17+
.idea

.pre-commit-config.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
repos:
2+
- repo: https://github.com/tekwizely/pre-commit-golang
3+
rev: master
4+
hooks:
5+
- id: go-fumpt
6+
- id: go-mod-tidy
7+
- id: go-lint
8+
- id: go-imports
9+
- repo: https://github.com/pre-commit/pre-commit-hooks
10+
rev: v2.3.0
11+
hooks:
12+
- id: check-yaml
13+
- id: end-of-file-fixer
14+
- id: trailing-whitespace

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Ndole Studio
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# go-http-client
2+
3+
[![Build](https://github.com/NdoleStudio/go-http-client/actions/workflows/main.yml/badge.svg)](https://github.com/NdoleStudio/go-http-client/actions/workflows/main.yml)
4+
[![codecov](https://codecov.io/gh/NdoleStudio/go-http-client/branch/main/graph/badge.svg)](https://codecov.io/gh/NdoleStudio/go-http-client)
5+
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/NdoleStudio/go-http-client/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/NdoleStudio/go-http-client/?branch=main)
6+
[![Go Report Card](https://goreportcard.com/badge/github.com/NdoleStudio/go-http-client)](https://goreportcard.com/report/github.com/NdoleStudio/go-http-client)
7+
[![GitHub contributors](https://img.shields.io/github/contributors/NdoleStudio/go-http-client)](https://github.com/NdoleStudio/go-http-client/graphs/contributors)
8+
[![GitHub license](https://img.shields.io/github/license/NdoleStudio/go-http-client?color=brightgreen)](https://github.com/NdoleStudio/go-http-client/blob/master/LICENSE)
9+
[![PkgGoDev](https://pkg.go.dev/badge/github.com/NdoleStudio/go-http-client)](https://pkg.go.dev/github.com/NdoleStudio/go-http-client)
10+
11+
12+
This package provides a generic `go` client template for an HTTP API
13+
14+
## Installation
15+
16+
`go-http-client` is compatible with modern Go releases in module mode, with Go installed:
17+
18+
```bash
19+
go get github.com/NdoleStudio/go-http-client
20+
```
21+
22+
Alternatively the same can be achieved if you use `import` in a package:
23+
24+
```go
25+
import "github.com/NdoleStudio/go-http-client"
26+
```
27+
28+
29+
## Implemented
30+
31+
- [Status Codes](#status-codes)
32+
- `GET /200`: OK
33+
34+
## Usage
35+
36+
### Initializing the Client
37+
38+
An instance of the client can be created using `New()`.
39+
40+
```go
41+
package main
42+
43+
import (
44+
"github.com/NdoleStudio/go-http-client"
45+
)
46+
47+
func main() {
48+
statusClient := client.New(client.WithDelay(200))
49+
}
50+
```
51+
52+
### Error handling
53+
54+
All API calls return an `error` as the last return object. All successful calls will return a `nil` error.
55+
56+
```go
57+
status, response, err := statusClient.Status.Ok(context.Background())
58+
if err != nil {
59+
//handle error
60+
}
61+
```
62+
63+
### Status Codes
64+
65+
#### `GET /200`: OK
66+
67+
```go
68+
status, response, err := statusClient.Status.Ok(context.Background())
69+
70+
if err != nil {
71+
log.Fatal(err)
72+
}
73+
74+
log.Println(status.Description) // OK
75+
```
76+
77+
## Testing
78+
79+
You can run the unit tests for this client from the root directory using the command below:
80+
81+
```bash
82+
go test -v
83+
```
84+
85+
## License
86+
87+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details

client.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package client
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"io/ioutil"
10+
"net/http"
11+
"strconv"
12+
)
13+
14+
type service struct {
15+
client *Client
16+
}
17+
18+
// Client is the campay API client.
19+
// Do not instantiate this client with Client{}. Use the New method instead.
20+
type Client struct {
21+
httpClient *http.Client
22+
common service
23+
baseURL string
24+
delay int
25+
26+
Status *statusService
27+
}
28+
29+
// New creates and returns a new campay.Client from a slice of campay.ClientOption.
30+
func New(options ...Option) *Client {
31+
config := defaultClientConfig()
32+
33+
for _, option := range options {
34+
option.apply(config)
35+
}
36+
37+
client := &Client{
38+
httpClient: config.httpClient,
39+
delay: config.delay,
40+
baseURL: config.baseURL,
41+
}
42+
43+
client.common.client = client
44+
client.Status = (*statusService)(&client.common)
45+
return client
46+
}
47+
48+
// newRequest creates an API request. A relative URL can be provided in uri,
49+
// in which case it is resolved relative to the BaseURL of the Client.
50+
// URI's should always be specified without a preceding slash.
51+
func (client *Client) newRequest(ctx context.Context, method, uri string, body interface{}) (*http.Request, error) {
52+
var buf io.ReadWriter
53+
if body != nil {
54+
buf = &bytes.Buffer{}
55+
enc := json.NewEncoder(buf)
56+
enc.SetEscapeHTML(false)
57+
err := enc.Encode(body)
58+
if err != nil {
59+
return nil, err
60+
}
61+
}
62+
63+
req, err := http.NewRequestWithContext(ctx, method, client.baseURL+uri, buf)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
req.Header.Set("Content-Type", "application/json")
69+
req.Header.Set("Accept", "application/json")
70+
71+
if client.delay > 0 {
72+
client.addURLParams(req, map[string]string{"sleep": strconv.Itoa(client.delay)})
73+
}
74+
75+
return req, nil
76+
}
77+
78+
// addURLParams adds urls parameters to an *http.Request
79+
func (client *Client) addURLParams(request *http.Request, params map[string]string) *http.Request {
80+
q := request.URL.Query()
81+
for key, value := range params {
82+
q.Add(key, value)
83+
}
84+
request.URL.RawQuery = q.Encode()
85+
return request
86+
}
87+
88+
// do carries out an HTTP request and returns a Response
89+
func (client *Client) do(req *http.Request) (*Response, error) {
90+
if req == nil {
91+
return nil, fmt.Errorf("%T cannot be nil", req)
92+
}
93+
94+
httpResponse, err := client.httpClient.Do(req)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
defer func() { _ = httpResponse.Body.Close() }()
100+
101+
resp, err := client.newResponse(httpResponse)
102+
if err != nil {
103+
return resp, err
104+
}
105+
106+
_, err = io.Copy(ioutil.Discard, httpResponse.Body)
107+
if err != nil {
108+
return resp, err
109+
}
110+
111+
return resp, nil
112+
}
113+
114+
// newResponse converts an *http.Response to *Response
115+
func (client *Client) newResponse(httpResponse *http.Response) (*Response, error) {
116+
if httpResponse == nil {
117+
return nil, fmt.Errorf("%T cannot be nil", httpResponse)
118+
}
119+
120+
resp := new(Response)
121+
resp.HTTPResponse = httpResponse
122+
123+
buf, err := ioutil.ReadAll(resp.HTTPResponse.Body)
124+
if err != nil {
125+
return nil, err
126+
}
127+
resp.Body = &buf
128+
129+
return resp, resp.Error()
130+
}

client_config.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package client
2+
3+
import "net/http"
4+
5+
type clientConfig struct {
6+
httpClient *http.Client
7+
delay int
8+
baseURL string
9+
}
10+
11+
func defaultClientConfig() *clientConfig {
12+
return &clientConfig{
13+
httpClient: http.DefaultClient,
14+
delay: 0,
15+
baseURL: "https://httpstat.us",
16+
}
17+
}

client_option.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package client
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
)
7+
8+
// Option is options for constructing a client
9+
type Option interface {
10+
apply(config *clientConfig)
11+
}
12+
13+
type clientOptionFunc func(config *clientConfig)
14+
15+
func (fn clientOptionFunc) apply(config *clientConfig) {
16+
fn(config)
17+
}
18+
19+
// WithHTTPClient sets the underlying HTTP client used for API requests.
20+
// By default, http.DefaultClient is used.
21+
func WithHTTPClient(httpClient *http.Client) Option {
22+
return clientOptionFunc(func(config *clientConfig) {
23+
if httpClient != nil {
24+
config.httpClient = httpClient
25+
}
26+
})
27+
}
28+
29+
// WithBaseURL set's the base url for the flutterwave API
30+
func WithBaseURL(baseURL string) Option {
31+
return clientOptionFunc(func(config *clientConfig) {
32+
if baseURL != "" {
33+
config.baseURL = strings.TrimRight(baseURL, "/")
34+
}
35+
})
36+
}
37+
38+
// WithDelay sets the delay in milliseconds before a response is gotten.
39+
// The delay must be > 0 for it to be used.
40+
func WithDelay(delay int) Option {
41+
return clientOptionFunc(func(config *clientConfig) {
42+
if delay > 0 {
43+
config.delay = delay
44+
}
45+
})
46+
}

0 commit comments

Comments
 (0)