Skip to content

Commit 382cd64

Browse files
committed
Refactor HTTP client logging and deprecation handling
1 parent aca1b14 commit 382cd64

File tree

10 files changed

+309
-125
lines changed

10 files changed

+309
-125
lines changed

internal/apihandlers/graph/graph_api_handler.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ type EndpointConfig struct {
106106
// It holds a Logger instance to facilitate logging across various API handling methods.
107107
// This handler is responsible for encoding and decoding request and response data,
108108
// determining content types, and other API interactions as defined by the APIHandler interface.
109-
type UnifiedGraphAPIHandler struct {
109+
type GraphAPIHandler struct {
110110
logger Logger // logger is used to output logs for the API handling processes.
111111
endpointAcceptedFormatsCache map[string][]string
112112
}
@@ -135,7 +135,7 @@ type APIHandler interface {
135135

136136
// GetAPIHandler initializes and returns an APIHandler with a configured logger.
137137
func GetAPIHandler(config Config) APIHandler {
138-
handler := &UnifiedGraphAPIHandler{}
138+
handler := &GraphAPIHandler{}
139139
logger := NewDefaultLogger()
140140
logger.SetLevel(config.LogLevel) // Use the LogLevel from the config
141141
handler.SetLogger(logger)
@@ -145,7 +145,7 @@ func GetAPIHandler(config Config) APIHandler {
145145
// SetLogger assigns a Logger instance to the UnifiedAPIHandler.
146146
// This allows for logging throughout the handler's operations,
147147
// enabling consistent logging that follows the configuration of the provided Logger.
148-
func (u *UnifiedGraphAPIHandler) SetLogger(logger Logger) {
148+
func (u *GraphAPIHandler) SetLogger(logger Logger) {
149149
u.logger = logger
150150
}
151151

@@ -157,7 +157,7 @@ func (u *UnifiedGraphAPIHandler) SetLogger(logger Logger) {
157157
// - For all url endpoints it defaults to "application/json" for the graph beta and V1.0 API's.
158158
// If the endpoint does not match any of the predefined patterns, "application/json" is used as a fallback.
159159
// This method logs the decision process at various stages for debugging purposes.
160-
func (u *UnifiedGraphAPIHandler) GetContentTypeHeader(endpoint string) string {
160+
func (u *GraphAPIHandler) GetContentTypeHeader(endpoint string) string {
161161
// Dynamic lookup from configuration should be the first priority
162162
for key, config := range configMap {
163163
if strings.HasPrefix(endpoint, key) {
@@ -188,7 +188,7 @@ func (u *UnifiedGraphAPIHandler) GetContentTypeHeader(endpoint string) string {
188188
//
189189
// Returns:
190190
// - The chosen Content-Type for the request, as a string.
191-
func (u *UnifiedGraphAPIHandler) GetContentTypeHeader(endpoint string) string {
191+
func (u *GraphAPIHandler) GetContentTypeHeader(endpoint string) string {
192192
// Initialize the cache if it's not already initialized
193193
if u.endpointAcceptedFormatsCache == nil {
194194
u.endpointAcceptedFormatsCache = make(map[string][]string)
@@ -275,7 +275,7 @@ func (u *UnifiedGraphAPIHandler) GetContentTypeHeader(endpoint string) string {
275275
// returns an error.
276276
// - It is the responsibility of the caller to handle any errors and to decide on the default action
277277
// if no formats are returned or in case of an error.
278-
func (u *UnifiedGraphAPIHandler) FetchSupportedRequestFormats(endpoint string) ([]string, error) {
278+
func (u *GraphAPIHandler) FetchSupportedRequestFormats(endpoint string) ([]string, error) {
279279
url := fmt.Sprintf("https://%s%s", DefaultBaseDomain, endpoint)
280280
req, err := http.NewRequest(http.MethodOptions, url, nil)
281281
if err != nil {
@@ -300,7 +300,7 @@ func (u *UnifiedGraphAPIHandler) FetchSupportedRequestFormats(endpoint string) (
300300
}
301301

302302
// MarshalRequest encodes the request body according to the endpoint for the API.
303-
func (u *UnifiedGraphAPIHandler) MarshalRequest(body interface{}, method string, endpoint string) ([]byte, error) {
303+
func (u *GraphAPIHandler) MarshalRequest(body interface{}, method string, endpoint string) ([]byte, error) {
304304
var (
305305
data []byte
306306
err error
@@ -341,7 +341,7 @@ func (u *UnifiedGraphAPIHandler) MarshalRequest(body interface{}, method string,
341341
}
342342

343343
// UnmarshalResponse decodes the response body from XML or JSON format depending on the Content-Type header.
344-
func (u *UnifiedGraphAPIHandler) UnmarshalResponse(resp *http.Response, out interface{}) error {
344+
func (u *GraphAPIHandler) UnmarshalResponse(resp *http.Response, out interface{}) error {
345345
// Handle DELETE method
346346
if resp.Request.Method == "DELETE" {
347347
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
@@ -449,7 +449,7 @@ func (u *UnifiedGraphAPIHandler) UnmarshalResponse(resp *http.Response, out inte
449449
// the server is informed of the client's versatile content handling capabilities while
450450
// indicating a preference for XML. The specified MIME types cover common content formats like
451451
// images, JSON, XML, HTML, plain text, and certificates, with a fallback option for all other types.
452-
func (u *UnifiedGraphAPIHandler) GetAcceptHeader() string {
452+
func (u *GraphAPIHandler) GetAcceptHeader() string {
453453
weightedAcceptHeader := "application/x-x509-ca-cert;q=0.95," +
454454
"application/pkix-cert;q=0.94," +
455455
"application/pem-certificate-chain;q=0.93," +
@@ -468,7 +468,7 @@ func (u *UnifiedGraphAPIHandler) GetAcceptHeader() string {
468468
}
469469

470470
// MarshalMultipartFormData takes a map with form fields and file paths and returns the encoded body and content type.
471-
func (u *UnifiedGraphAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string) ([]byte, string, error) {
471+
func (u *GraphAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string) ([]byte, string, error) {
472472
body := &bytes.Buffer{}
473473
writer := multipart.NewWriter(body)
474474

@@ -506,7 +506,7 @@ func (u *UnifiedGraphAPIHandler) MarshalMultipartRequest(fields map[string]strin
506506
}
507507

508508
// handleBinaryData checks if the response should be treated as binary data and assigns to out if so.
509-
func (u *UnifiedGraphAPIHandler) handleBinaryData(contentType, contentDisposition string, bodyBytes []byte, out interface{}) error {
509+
func (u *GraphAPIHandler) handleBinaryData(contentType, contentDisposition string, bodyBytes []byte, out interface{}) error {
510510
if strings.Contains(contentType, "application/octet-stream") || strings.HasPrefix(contentDisposition, "attachment") {
511511
if outPointer, ok := out.(*[]byte); ok {
512512
*outPointer = bodyBytes
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package jamfpro
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"strings"
7+
8+
"github.com/PuerkitoBio/goquery"
9+
)
10+
11+
// ExtractErrorMessageFromHTML attempts to parse an HTML error page and extract a human-readable error message.
12+
func ExtractErrorMessageFromHTML(htmlContent string) string {
13+
r := bytes.NewReader([]byte(htmlContent))
14+
doc, err := goquery.NewDocumentFromReader(r)
15+
if err != nil {
16+
return "Unable to parse HTML content"
17+
}
18+
19+
var messages []string
20+
doc.Find("p").Each(func(i int, s *goquery.Selection) {
21+
messages = append(messages, s.Text())
22+
})
23+
24+
return strings.Join(messages, " | ")
25+
}
26+
27+
// ParseJSONErrorResponse parses the JSON error message from the response body.
28+
func ParseJSONErrorResponse(body []byte) (string, error) {
29+
var errorResponse struct {
30+
HTTPStatus int `json:"httpStatus"`
31+
Errors []struct {
32+
Code string `json:"code"`
33+
Description string `json:"description"`
34+
ID string `json:"id"`
35+
Field string `json:"field"`
36+
} `json:"errors"`
37+
}
38+
39+
err := json.Unmarshal(body, &errorResponse)
40+
if err != nil {
41+
return "", err
42+
}
43+
44+
if len(errorResponse.Errors) > 0 {
45+
return errorResponse.Errors[0].Description, nil
46+
}
47+
48+
return "No error description available", nil
49+
}

internal/apihandlers/jamfpro/jamfpro_api_handler.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ type EndpointConfig struct {
120120
// It holds a Logger instance to facilitate logging across various API handling methods.
121121
// This handler is responsible for encoding and decoding request and response data,
122122
// determining content types, and other API interactions as defined by the APIHandler interface.
123-
type UnifiedJamfAPIHandler struct {
123+
type JamfAPIHandler struct {
124124
logger Logger // logger is used to output logs for the API handling processes.
125125
}
126126

@@ -151,6 +151,7 @@ func (c *Client) ConstructAPIAuthEndpoint(endpointPath string) string {
151151
return url
152152
}
153153

154+
/*
154155
// APIHandler is an interface for encoding, decoding, and determining content types for different API implementations.
155156
// It encapsulates behavior for encoding and decoding requests and responses.
156157
type APIHandler interface {
@@ -161,10 +162,10 @@ type APIHandler interface {
161162
GetAcceptHeader() string
162163
SetLogger(logger Logger)
163164
}
164-
165+
*/
165166
// GetAPIHandler initializes and returns an APIHandler with a configured logger.
166167
func GetAPIHandler(config Config) APIHandler {
167-
handler := &UnifiedJamfAPIHandler{}
168+
handler := &JamfAPIHandler{}
168169
logger := NewDefaultLogger()
169170
logger.SetLevel(config.LogLevel) // Use the LogLevel from the config
170171
handler.SetLogger(logger)
@@ -174,7 +175,7 @@ func GetAPIHandler(config Config) APIHandler {
174175
// SetLogger assigns a Logger instance to the UnifiedAPIHandler.
175176
// This allows for logging throughout the handler's operations,
176177
// enabling consistent logging that follows the configuration of the provided Logger.
177-
func (u *UnifiedJamfAPIHandler) SetLogger(logger Logger) {
178+
func (u *JamfAPIHandler) SetLogger(logger Logger) {
178179
u.logger = logger
179180
}
180181

@@ -186,7 +187,7 @@ func (u *UnifiedJamfAPIHandler) SetLogger(logger Logger) {
186187
// - For url endpoints starting with "/api", it defaults to "application/json" for the JamfPro API.
187188
// If the endpoint does not match any of the predefined patterns, "application/json" is used as a fallback.
188189
// This method logs the decision process at various stages for debugging purposes.
189-
func (u *UnifiedJamfAPIHandler) GetContentTypeHeader(endpoint string) string {
190+
func (u *JamfAPIHandler) GetContentTypeHeader(endpoint string) string {
190191
// Dynamic lookup from configuration should be the first priority
191192
for key, config := range configMap {
192193
if strings.HasPrefix(endpoint, key) {
@@ -215,7 +216,7 @@ func (u *UnifiedJamfAPIHandler) GetContentTypeHeader(endpoint string) string {
215216
}
216217

217218
// MarshalRequest encodes the request body according to the endpoint for the API.
218-
func (u *UnifiedJamfAPIHandler) MarshalRequest(body interface{}, method string, endpoint string) ([]byte, error) {
219+
func (u *JamfAPIHandler) MarshalRequest(body interface{}, method string, endpoint string) ([]byte, error) {
219220
var (
220221
data []byte
221222
err error
@@ -256,7 +257,7 @@ func (u *UnifiedJamfAPIHandler) MarshalRequest(body interface{}, method string,
256257
}
257258

258259
// UnmarshalResponse decodes the response body from XML or JSON format depending on the Content-Type header.
259-
func (u *UnifiedJamfAPIHandler) UnmarshalResponse(resp *http.Response, out interface{}) error {
260+
func (u *JamfAPIHandler) UnmarshalResponse(resp *http.Response, out interface{}) error {
260261
// Handle DELETE method
261262
if resp.Request.Method == "DELETE" {
262263
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
@@ -350,7 +351,7 @@ func (u *UnifiedJamfAPIHandler) UnmarshalResponse(resp *http.Response, out inter
350351
// the server is informed of the client's versatile content handling capabilities while
351352
// indicating a preference for XML. The specified MIME types cover common content formats like
352353
// images, JSON, XML, HTML, plain text, and certificates, with a fallback option for all other types.
353-
func (u *UnifiedJamfAPIHandler) GetAcceptHeader() string {
354+
func (u *JamfAPIHandler) GetAcceptHeader() string {
354355
weightedAcceptHeader := "application/x-x509-ca-cert;q=0.95," +
355356
"application/pkix-cert;q=0.94," +
356357
"application/pem-certificate-chain;q=0.93," +
@@ -369,7 +370,7 @@ func (u *UnifiedJamfAPIHandler) GetAcceptHeader() string {
369370
}
370371

371372
// MarshalMultipartFormData takes a map with form fields and file paths and returns the encoded body and content type.
372-
func (u *UnifiedJamfAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string) ([]byte, string, error) {
373+
func (u *JamfAPIHandler) MarshalMultipartRequest(fields map[string]string, files map[string]string) ([]byte, string, error) {
373374
body := &bytes.Buffer{}
374375
writer := multipart.NewWriter(body)
375376

@@ -407,7 +408,7 @@ func (u *UnifiedJamfAPIHandler) MarshalMultipartRequest(fields map[string]string
407408
}
408409

409410
// handleBinaryData checks if the response should be treated as binary data and assigns to out if so.
410-
func (u *UnifiedJamfAPIHandler) handleBinaryData(contentType, contentDisposition string, bodyBytes []byte, out interface{}) error {
411+
func (u *JamfAPIHandler) handleBinaryData(contentType, contentDisposition string, bodyBytes []byte, out interface{}) error {
411412
if strings.Contains(contentType, "application/octet-stream") || strings.HasPrefix(contentDisposition, "attachment") {
412413
if outPointer, ok := out.(*[]byte); ok {
413414
*outPointer = bodyBytes

internal/httpclient/api_handler.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// http_client_auth_token_management.go
2+
package httpclient
3+
4+
import (
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/deploymenttheory/go-api-sdk-jamfpro/internal/apihandlers/graph"
9+
"github.com/deploymenttheory/go-api-sdk-jamfpro/internal/apihandlers/jamfpro"
10+
)
11+
12+
// APIHandler is an interface for encoding, decoding, and determining content types for different API implementations.
13+
// It encapsulates behavior for encoding and decoding requests and responses.
14+
type APIHandler interface {
15+
MarshalRequest(body interface{}, method string, endpoint string) ([]byte, error)
16+
MarshalMultipartRequest(fields map[string]string, files map[string]string) ([]byte, string, error) // New method for multipart
17+
UnmarshalResponse(resp *http.Response, out interface{}) error
18+
GetContentTypeHeader(method string) string
19+
GetAcceptHeader() string
20+
}
21+
22+
// LoadAPIHandler returns an APIHandler based on the provided API type.
23+
// 'apiType' parameter could be "jamf" or "graph" to specify which API handler to load.
24+
func LoadAPIHandler(config Config, apiType string) (APIHandler, error) {
25+
var apiHandler APIHandler
26+
switch apiType {
27+
case "jamfpro":
28+
// Assuming GetAPIHandler returns a JamfAPIHandler
29+
apiHandler = &jamfpro.JamfAPIHandler{
30+
// Initialize with necessary parameters
31+
}
32+
case "graph":
33+
// Assuming GetAPIHandler returns a GraphAPIHandler
34+
apiHandler = &graph.GraphAPIHandler{
35+
// Initialize with necessary parameters
36+
}
37+
default:
38+
return nil, fmt.Errorf("unsupported API type: %s", apiType)
39+
}
40+
41+
// Set the logger level for the handler if needed
42+
logger := NewDefaultLogger() // Or use config.Logger if it's not nil
43+
logger.SetLevel(config.LogLevel)
44+
45+
return apiHandler, nil
46+
}

0 commit comments

Comments
 (0)