Skip to content

Commit a10afd9

Browse files
authored
Merge pull request #258 from deploymenttheory/dev-jl
Interfaced HTTP Operations (stage 1) to facilitate unit testing
2 parents 1c671d4 + 87ef416 commit a10afd9

File tree

10 files changed

+59
-195
lines changed

10 files changed

+59
-195
lines changed

go.mod

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@ go 1.22.4
55
require (
66
github.com/antchfx/xmlquery v1.4.1
77
github.com/google/uuid v1.6.0
8-
github.com/stretchr/testify v1.9.0
98
go.uber.org/zap v1.27.0
109
golang.org/x/net v0.26.0
1110
)
1211

1312
require (
1413
github.com/antchfx/xpath v1.3.1 // indirect
15-
github.com/davecgh/go-spew v1.1.1 // indirect
1614
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
17-
github.com/pmezard/go-difflib v1.0.0 // indirect
15+
github.com/stretchr/testify v1.9.0 // indirect
1816
go.uber.org/multierr v1.11.0 // indirect
1917
golang.org/x/text v0.16.0 // indirect
20-
gopkg.in/yaml.v3 v3.0.1 // indirect
2118
)

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,5 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
4949
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
5050
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
5151
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
52-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
53-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5452
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5553
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

httpclient/client.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,37 @@ package httpclient
99

1010
import (
1111
"fmt"
12+
"io"
1213
"net/http"
14+
"net/url"
1315
"time"
1416

1517
"github.com/deploymenttheory/go-api-http-client/concurrency"
1618
"go.uber.org/zap"
1719
)
1820

19-
const ()
20-
21-
// TODO all struct comments
21+
type httpExecutor interface {
22+
// Inherited
23+
CloseIdleConnections()
24+
Do(req *http.Request) (*http.Response, error)
25+
Get(url string) (resp *http.Response, err error)
26+
Head(url string) (resp *http.Response, err error)
27+
Post(url string, contentType string, body io.Reader) (resp *http.Response, err error)
28+
PostForm(url string, data url.Values) (resp *http.Response, err error)
29+
30+
// Additional
31+
SetCookieJar(jar http.CookieJar)
32+
SetCookies(url *url.URL, cookies []*http.Cookie)
33+
SetCustomTimeout(time.Duration)
34+
Cookies(*url.URL) []*http.Cookie
35+
SetRedirectPolicy(*func(req *http.Request, via []*http.Request) error)
36+
}
2237

2338
// Master struct/object
2439
type Client struct {
2540
config *ClientConfig
2641
Integration *APIIntegration
27-
http *http.Client
42+
http httpExecutor
2843
Sugar *zap.SugaredLogger
2944
Concurrency *concurrency.ConcurrencyHandler
3045
}
@@ -104,12 +119,10 @@ func (c *ClientConfig) Build() (*Client, error) {
104119

105120
c.Sugar.Debug("configuration valid")
106121

107-
httpClient := &http.Client{
108-
Timeout: c.CustomTimeout,
109-
}
122+
httpClient := &prodClient{}
110123

111124
if c.CustomRedirectPolicy != nil {
112-
httpClient.CheckRedirect = *c.CustomRedirectPolicy
125+
httpClient.SetRedirectPolicy(c.CustomRedirectPolicy)
113126
}
114127

115128
// TODO refactor concurrency

httpclient/cookies.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ func (c *Client) loadCustomCookies() error {
1414
return err
1515
}
1616

17-
c.http.Jar = cookieJar
17+
c.http.SetCookieJar(cookieJar)
1818

1919
cookieUrl, err := url.Parse((*c.Integration).GetFQDN())
2020
c.Sugar.Debug("cookie URL set globally to: %s", cookieUrl)
2121
if err != nil {
2222
return err
2323
}
2424

25-
c.http.Jar.SetCookies(cookieUrl, c.config.CustomCookies)
25+
c.http.SetCookies(cookieUrl, c.config.CustomCookies)
2626

2727
if c.config.HideSensitiveData {
2828
c.Sugar.Debug("[REDACTED] cookies set successfully")
2929
} else {
30-
c.Sugar.Debug("custom cookies set: %v", c.http.Jar.Cookies(cookieUrl))
30+
c.Sugar.Debug("custom cookies set: %v", c.http.Cookies(cookieUrl))
3131
}
3232

3333
return nil

httpclient/http.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package httpclient
2+
3+
import (
4+
"net/http"
5+
"net/url"
6+
"time"
7+
)
8+
9+
type prodClient struct {
10+
*http.Client
11+
}
12+
13+
func (c *prodClient) SetCookieJar(jar http.CookieJar) {
14+
c.Jar = jar
15+
}
16+
17+
func (c *prodClient) SetCookies(url *url.URL, cookies []*http.Cookie) {
18+
c.Jar.SetCookies(url, cookies)
19+
}
20+
21+
func (c *prodClient) SetCustomTimeout(timeout time.Duration) {
22+
c.Timeout = timeout
23+
}
24+
25+
func (c *prodClient) Cookies(url *url.URL) []*http.Cookie {
26+
return c.Jar.Cookies(url)
27+
}
28+
29+
func (c *prodClient) SetRedirectPolicy(policy *func(req *http.Request, via []*http.Request) error) {
30+
c.CheckRedirect = *policy
31+
}

httpclient/request.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
// - endpoint: The target API endpoint for the request. This should be a relative path that will be appended to the base URL
2828
// configured for the HTTP client.
2929
// - body: The payload for the request, which will be serialized into the request body. The serialization format (e.g., JSON, XML)
30-
// is determined by the content-type header and the specific implementation of the API handler used by the client.
30+
// is determined by the content-type header and the specific implementation of the API handler used by the client.
3131
// - out: A pointer to an output variable where the response will be deserialized. The function expects this to be a pointer to
3232
// a struct that matches the expected response schema.
3333
//
@@ -238,7 +238,7 @@ func (c *Client) request(ctx context.Context, method, endpoint string, body inte
238238
if c.config.EnableConcurrencyManagement {
239239
_, requestID, err := c.Concurrency.AcquireConcurrencyPermit(ctx)
240240
if err != nil {
241-
return nil, fmt.Errorf("Failed to acquire concurrency permit: %v", err)
241+
return nil, fmt.Errorf("failed to acquire concurrency permit: %v", err)
242242

243243
}
244244

httpclient/utility.go

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
package httpclient
33

44
import (
5-
"errors"
65
"fmt"
76
"net/http"
87
"os"
98
"path/filepath"
10-
"regexp"
119
"strconv"
1210
"strings"
1311
"time"
@@ -38,52 +36,6 @@ func validateFilePath(path string) (string, error) {
3836

3937
}
4038

41-
// validateClientID checks if a client ID is a valid UUID.
42-
func validateValidClientID(clientID string) error {
43-
uuidRegex := `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`
44-
if regexp.MustCompile(uuidRegex).MatchString(clientID) {
45-
return nil
46-
}
47-
return errors.New("clientID failed regex check")
48-
}
49-
50-
func validateClientSecret(clientSecret string) error {
51-
if len(clientSecret) < 16 {
52-
return errors.New("client secret must be at least 16 characters long")
53-
}
54-
55-
if matched, _ := regexp.MatchString(`[a-z]`, clientSecret); !matched {
56-
return errors.New("client secret must contain at least one lowercase letter")
57-
}
58-
59-
if matched, _ := regexp.MatchString(`[A-Z]`, clientSecret); !matched {
60-
return errors.New("client secret must contain at least one uppercase letter")
61-
}
62-
63-
if matched, _ := regexp.MatchString(`\d`, clientSecret); !matched {
64-
return errors.New("client secret must contain at least one digit")
65-
}
66-
67-
return nil
68-
}
69-
70-
// validateUsername checks if a username meets the minimum requirements.
71-
func validateUsername(username string) error {
72-
usernameRegex := `^[a-zA-Z0-9!@#$%^&*()_\-\+=\[\]{\}\\|;:'",<.>/?]+$`
73-
if !regexp.MustCompile(usernameRegex).MatchString(username) {
74-
return errors.New("username failed regex test")
75-
}
76-
return nil
77-
}
78-
79-
// validatePassword checks if a password meets the minimum requirements.
80-
func validatePassword(password string) error {
81-
if len(password) < 8 {
82-
return errors.New("password not long enough")
83-
}
84-
return nil
85-
}
86-
8739
// getEnvAsString reads an environment variable as a string, with a fallback default value.
8840
func getEnvAsString(name string, defaultVal string) string {
8941
if value, exists := os.LookupEnv(name); exists {

mock/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package mock

response/t_parse_test.go

Lines changed: 0 additions & 82 deletions
This file was deleted.

response/t_success_test.go

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)