Skip to content

Commit a94afe7

Browse files
authored
Merge pull request #61 from deploymenttheory/dev
Dev
2 parents 62e2c68 + d3bc0dc commit a94afe7

21 files changed

+1119
-563
lines changed

apihandlers/graph/graph_api_error_messages.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import (
1111
"github.com/PuerkitoBio/goquery"
1212
)
1313

14-
// APIHandlerError represents an error response from the Jamf Pro API.
14+
// APIHandlerError represents an error response from the graph API.
1515
type APIHandlerError struct {
1616
HTTPStatusCode int `json:"httpStatusCode"`
1717
ErrorType string `json:"errorType"`
1818
ErrorMessage string `json:"errorMessage"`
1919
ExtraDetails map[string]interface{} `json:"extraDetails"`
2020
}
2121

22-
// ReturnAPIErrorResponse parses an HTTP error response from the Jamf Pro API.
22+
// ReturnAPIErrorResponse parses an HTTP error response from the graph API.
2323
func (g *GraphAPIHandler) ReturnAPIErrorResponse(resp *http.Response) *APIHandlerError {
2424
var errorMessage, errorType string
2525
var extraDetails map[string]interface{}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ go 1.21
55
require (
66
github.com/PuerkitoBio/goquery v1.8.1
77
github.com/google/uuid v1.6.0
8+
github.com/stretchr/testify v1.8.1
89
go.uber.org/zap v1.26.0
910
)
1011

1112
require (
1213
github.com/andybalholm/cascadia v1.3.1 // indirect
14+
github.com/davecgh/go-spew v1.1.1 // indirect
15+
github.com/pmezard/go-difflib v1.0.0 // indirect
16+
github.com/stretchr/objx v0.5.0 // indirect
1317
go.uber.org/multierr v1.10.0 // indirect
1418
golang.org/x/net v0.19.0 // indirect
19+
gopkg.in/yaml.v3 v3.0.1 // indirect
1520
)

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc
22
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
33
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
44
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
56
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
67
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
78
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
89
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
910
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1011
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
12+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
13+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
14+
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
15+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
16+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
17+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
1118
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
1219
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
1320
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -48,5 +55,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
4855
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
4956
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
5057
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
58+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
59+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
60+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
5161
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5262
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

httpclient/httpclient_api_handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// api_handler.go
1+
// httpclient_api_handler.go
22
package httpclient
33

44
import (
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// httpclient_api_handler.go
2+
package httpclient
3+
4+
import (
5+
"errors"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/deploymenttheory/go-api-http-client/apihandlers/graph"
10+
"github.com/deploymenttheory/go-api-http-client/apihandlers/jamfpro"
11+
"github.com/deploymenttheory/go-api-http-client/logger"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/mock"
14+
)
15+
16+
// NewMockAPIHandler creates a new instance of MockAPIHandler.
17+
func NewMockAPIHandler() *MockAPIHandler {
18+
return &MockAPIHandler{}
19+
}
20+
21+
// Implement each method of the APIHandler interface on MockAPIHandler.
22+
23+
func (m *MockAPIHandler) ConstructAPIResourceEndpoint(instanceName string, endpointPath string, log logger.Logger) string {
24+
args := m.Called(instanceName, endpointPath, log)
25+
return args.String(0)
26+
}
27+
28+
func (m *MockAPIHandler) ConstructAPIAuthEndpoint(instanceName string, endpointPath string, log logger.Logger) string {
29+
args := m.Called(instanceName, endpointPath, log)
30+
return args.String(0)
31+
}
32+
33+
func (m *MockAPIHandler) MarshalRequest(body interface{}, method string, endpoint string, log logger.Logger) ([]byte, error) {
34+
args := m.Called(body, method, endpoint, log)
35+
return args.Get(0).([]byte), args.Error(1)
36+
}
37+
38+
func (m *MockAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string, log logger.Logger) ([]byte, string, error) {
39+
args := m.Called(fields, files, log)
40+
return args.Get(0).([]byte), args.String(1), args.Error(2)
41+
}
42+
43+
func (m *MockAPIHandler) HandleAPISuccessResponse(resp *http.Response, out interface{}, log logger.Logger) error {
44+
args := m.Called(resp, out, log)
45+
return args.Error(0)
46+
}
47+
48+
func (m *MockAPIHandler) HandleAPIErrorResponse(resp *http.Response, out interface{}, log logger.Logger) error {
49+
args := m.Called(resp, out, log)
50+
return args.Error(0)
51+
}
52+
53+
// TestLoadAPIHandler verifies the functionality of the LoadAPIHandler function in the httpclient package.
54+
// This function is designed to return the appropriate APIHandler implementation based on the provided apiType argument.
55+
// The test cases cover the following scenarios:
56+
// 1. Loading a JamfPro API handler by providing "jamfpro" as the apiType.
57+
// 2. Loading a Graph API handler by providing "graph" as the apiType.
58+
// 3. Handling an unsupported API type by providing an unknown apiType, which should result in an error.
59+
func TestLoadAPIHandler(t *testing.T) {
60+
mockLogger := new(MockLogger)
61+
tests := []struct {
62+
name string
63+
apiType string
64+
wantType interface{}
65+
wantErr bool
66+
}{
67+
{"Load JamfPro Handler", "jamfpro", &jamfpro.JamfAPIHandler{}, false},
68+
{"Load Graph Handler", "graph", &graph.GraphAPIHandler{}, false},
69+
{"Unsupported API Type", "unknown", nil, true},
70+
}
71+
72+
for _, tt := range tests {
73+
t.Run(tt.name, func(t *testing.T) {
74+
if tt.wantErr {
75+
mockLogger.On("Error", mock.Anything, mock.Anything).Return(errors.New("Unsupported API type")).Once()
76+
} else {
77+
mockLogger.On("Info", mock.Anything, mock.Anything).Once()
78+
}
79+
80+
got, err := LoadAPIHandler(tt.apiType, mockLogger)
81+
if tt.wantErr {
82+
assert.Error(t, err)
83+
} else {
84+
assert.NoError(t, err)
85+
assert.IsType(t, tt.wantType, got)
86+
}
87+
88+
mockLogger.AssertExpectations(t)
89+
})
90+
}
91+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package httpclient
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/mock"
9+
)
10+
11+
// MockAPIHandler is a mock type for the APIHandler interface
12+
type MockAPIHandler struct {
13+
mock.Mock
14+
}
15+
16+
// GetAPIRequestHeaders mocks the GetAPIRequestHeaders method of the APIHandler
17+
func (_m *MockAPIHandler) GetAPIRequestHeaders(endpoint string) map[string]string {
18+
ret := _m.Called(endpoint)
19+
20+
var r0 map[string]string
21+
if rf, ok := ret.Get(0).(func(string) map[string]string); ok {
22+
r0 = rf(endpoint)
23+
} else {
24+
r0 = ret.Get(0).(map[string]string)
25+
}
26+
27+
return r0
28+
}
29+
30+
// TestSetAuthorization tests the SetAuthorization method
31+
func TestSetAuthorization(t *testing.T) {
32+
req, _ := http.NewRequest("GET", "http://example.com", nil)
33+
logger := NewMockLogger() // Assuming you have a mock logger
34+
hm := NewHeaderManager(req, logger, nil, "")
35+
36+
// Test without Bearer prefix
37+
hm.SetAuthorization("token123")
38+
assert.Equal(t, "Bearer token123", req.Header.Get("Authorization"))
39+
40+
// Test with Bearer prefix
41+
hm.SetAuthorization("Bearer token123")
42+
assert.Equal(t, "Bearer token123", req.Header.Get("Authorization"))
43+
}
44+
45+
// TestSetContentType tests the SetContentType method
46+
func TestSetContentType(t *testing.T) {
47+
req, _ := http.NewRequest("GET", "http://example.com", nil)
48+
logger := NewMockLogger() // Assuming you have a mock logger
49+
hm := NewHeaderManager(req, logger, nil, "")
50+
51+
hm.SetContentType("application/json")
52+
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
53+
}
54+
55+
// TestSetAccept tests the SetAccept method
56+
func TestSetAccept(t *testing.T) {
57+
req, _ := http.NewRequest("GET", "http://example.com", nil)
58+
logger := NewMockLogger() // Assuming you have a mock logger
59+
hm := NewHeaderManager(req, logger, nil, "")
60+
61+
hm.SetAccept("application/json")
62+
assert.Equal(t, "application/json", req.Header.Get("Accept"))
63+
}
64+
65+
// TestSetUserAgent tests the SetUserAgent method
66+
func TestSetUserAgent(t *testing.T) {
67+
req, _ := http.NewRequest("GET", "http://example.com", nil)
68+
logger := NewMockLogger() // Assuming you have a mock logger
69+
hm := NewHeaderManager(req, logger, nil, "")
70+
71+
hm.SetUserAgent("CustomUserAgent/1.0")
72+
assert.Equal(t, "CustomUserAgent/1.0", req.Header.Get("User-Agent"))
73+
}
74+
75+
/*
76+
// TestSetRequestHeaders tests the SetRequestHeaders method
77+
func TestSetRequestHeaders(t *testing.T) {
78+
req, _ := http.NewRequest("GET", "http://example.com", nil)
79+
logger := NewMockLogger() // Assuming you have a mock logger
80+
mockAPIHandler := new(MockAPIHandler)
81+
hm := NewHeaderManager(req, logger, mockAPIHandler, "token123")
82+
83+
// Setup expectations
84+
mockAPIHandler.On("GetAPIRequestHeaders", "testEndpoint").Return(map[string]string{
85+
"Authorization": "",
86+
"X-Custom-Header": "CustomValue",
87+
})
88+
89+
hm.SetRequestHeaders("testEndpoint")
90+
91+
// Assertions
92+
assert.Equal(t, "Bearer token123", req.Header.Get("Authorization"))
93+
assert.Equal(t, "CustomValue", req.Header.Get("X-Custom-Header"))
94+
mockAPIHandler.AssertExpectations(t)
95+
}
96+
*/
97+
// TestSetCacheControlHeader tests the SetCacheControlHeader function
98+
func TestSetCacheControlHeader(t *testing.T) {
99+
req, _ := http.NewRequest("GET", "http://example.com", nil)
100+
101+
SetCacheControlHeader(req, "no-cache")
102+
assert.Equal(t, "no-cache", req.Header.Get("Cache-Control"))
103+
}
104+
105+
// TestSetConditionalHeaders tests the SetConditionalHeaders function
106+
func TestSetConditionalHeaders(t *testing.T) {
107+
req, _ := http.NewRequest("GET", "http://example.com", nil)
108+
109+
SetConditionalHeaders(req, "Wed, 21 Oct 2015 07:28:00 GMT", "etagValue")
110+
assert.Equal(t, "Wed, 21 Oct 2015 07:28:00 GMT", req.Header.Get("If-Modified-Since"))
111+
assert.Equal(t, "etagValue", req.Header.Get("If-None-Match"))
112+
}
113+
114+
// TestSetAcceptEncodingHeader tests the SetAcceptEncodingHeader function
115+
func TestSetAcceptEncodingHeader(t *testing.T) {
116+
req, _ := http.NewRequest("GET", "http://example.com", nil)
117+
118+
SetAcceptEncodingHeader(req, "gzip, deflate")
119+
assert.Equal(t, "gzip, deflate", req.Header.Get("Accept-Encoding"))
120+
}
121+
122+
// TestSetRefererHeader tests the SetRefererHeader function
123+
func TestSetRefererHeader(t *testing.T) {
124+
req, _ := http.NewRequest("GET", "http://example.com", nil)
125+
126+
SetRefererHeader(req, "http://referrer.example.com")
127+
assert.Equal(t, "http://referrer.example.com", req.Header.Get("Referer"))
128+
}
129+
130+
// TestSetXForwardedForHeader tests the SetXForwardedForHeader function
131+
func TestSetXForwardedForHeader(t *testing.T) {
132+
req, _ := http.NewRequest("GET", "http://example.com", nil)
133+
134+
SetXForwardedForHeader(req, "client1, proxy1, proxy2")
135+
assert.Equal(t, "client1, proxy1, proxy2", req.Header.Get("X-Forwarded-For"))
136+
}
137+
138+
// TestSetCustomHeader tests the ability to set arbitrary headers
139+
func TestSetCustomHeader(t *testing.T) {
140+
req, _ := http.NewRequest("GET", "http://example.com", nil)
141+
142+
SetCustomHeader(req, "X-Custom-Header", "CustomValue")
143+
assert.Equal(t, "CustomValue", req.Header.Get("X-Custom-Header"))
144+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// http_helpers.go
2+
package httpclient
3+
4+
import (
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
// TestParseISO8601Date tests the ParseISO8601Date function with various date strings
12+
func TestParseISO8601Date(t *testing.T) {
13+
tests := []struct {
14+
dateStr string
15+
expectErr bool
16+
expectedTime time.Time // Add an expectedTime field for successful parsing
17+
}{
18+
{
19+
dateStr: "2023-01-02T15:04:05Z",
20+
expectErr: false,
21+
expectedTime: time.Date(2023, time.January, 2, 15, 4, 5, 0, time.UTC),
22+
},
23+
{
24+
dateStr: "2023-01-02T15:04:05-07:00",
25+
expectErr: false,
26+
expectedTime: time.Date(2023, time.January, 2, 15, 4, 5, 0, time.FixedZone("", -7*3600)),
27+
},
28+
{
29+
dateStr: "invalid-date",
30+
expectErr: true,
31+
},
32+
}
33+
34+
for _, tt := range tests {
35+
t.Run(tt.dateStr, func(t *testing.T) {
36+
result, err := ParseISO8601Date(tt.dateStr)
37+
38+
if tt.expectErr {
39+
assert.Error(t, err, "Expected an error for date string: "+tt.dateStr)
40+
} else {
41+
assert.NoError(t, err, "Did not expect an error for date string: "+tt.dateStr)
42+
assert.True(t, result.Equal(tt.expectedTime), "Parsed time should match expected time")
43+
}
44+
})
45+
}
46+
}
47+
48+
// TestRedactSensitiveData tests the RedactSensitiveData function with various scenarios
49+
func TestRedactSensitiveData(t *testing.T) {
50+
tests := []struct {
51+
name string
52+
hideSensitive bool
53+
key string
54+
value string
55+
expectedOutcome string
56+
}{
57+
{"RedactSensitiveKey", true, "AccessToken", "secret-token", "REDACTED"},
58+
{"RedactSensitiveKeyAuthorization", true, "Authorization", "Bearer secret-token", "REDACTED"},
59+
{"DoNotRedactNonSensitiveKey", true, "NonSensitiveKey", "non-sensitive-value", "non-sensitive-value"},
60+
{"DoNotRedactWhenDisabled", false, "AccessToken", "secret-token", "secret-token"},
61+
}
62+
63+
for _, tt := range tests {
64+
t.Run(tt.name, func(t *testing.T) {
65+
client := &Client{
66+
clientConfig: ClientConfig{
67+
ClientOptions: ClientOptions{
68+
HideSensitiveData: tt.hideSensitive,
69+
},
70+
},
71+
}
72+
73+
result := RedactSensitiveData(client, tt.key, tt.value)
74+
assert.Equal(t, tt.expectedOutcome, result, "Redaction outcome should match expected")
75+
})
76+
}
77+
}

0 commit comments

Comments
 (0)