Skip to content

Commit 0533ae5

Browse files
authored
Merge pull request #132 from deploymenttheory/dev
Updates the error logging
2 parents cd24af1 + 28237d2 commit 0533ae5

File tree

3 files changed

+51
-40
lines changed

3 files changed

+51
-40
lines changed

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.21
44

55
require (
66
github.com/PuerkitoBio/goquery v1.9.1
7+
github.com/antchfx/xmlquery v1.3.18
78
github.com/google/uuid v1.6.0
89
github.com/stretchr/testify v1.9.0
910
go.uber.org/zap v1.27.0
@@ -12,10 +13,13 @@ require (
1213

1314
require (
1415
github.com/andybalholm/cascadia v1.3.2 // indirect
16+
github.com/antchfx/xpath v1.2.4 // indirect
1517
github.com/davecgh/go-spew v1.1.1 // indirect
18+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
1619
github.com/pmezard/go-difflib v1.0.0 // indirect
1720
github.com/stretchr/objx v0.5.2 // indirect
1821
go.uber.org/multierr v1.10.0 // indirect
1922
golang.org/x/sys v0.18.0 // indirect
23+
golang.org/x/text v0.14.0 // indirect
2024
gopkg.in/yaml.v3 v3.0.1 // indirect
2125
)

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VP
22
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
33
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
44
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
5+
github.com/antchfx/xmlquery v1.3.18 h1:FSQ3wMuphnPPGJOFhvc+cRQ2CT/rUj4cyQXkJcjOwz0=
6+
github.com/antchfx/xmlquery v1.3.18/go.mod h1:Afkq4JIeXut75taLSuI31ISJ/zeq+3jG7TunF7noreA=
7+
github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY=
8+
github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
59
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
610
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
12+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
713
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
814
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
915
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -27,6 +33,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
2733
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
2834
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
2935
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
36+
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
3037
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
3138
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
3239
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
@@ -51,6 +58,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
5158
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
5259
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
5360
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
61+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
62+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
5463
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
5564
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
5665
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

response/error.go

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ package response
55
import (
66
"bytes"
77
"encoding/json"
8-
"encoding/xml"
98
"fmt"
109
"io"
1110
"net/http"
1211
"strings"
1312

13+
"github.com/antchfx/xmlquery"
1414
"github.com/deploymenttheory/go-api-http-client/logger"
1515
"golang.org/x/net/html"
1616
)
@@ -61,16 +61,12 @@ func HandleAPIErrorResponse(resp *http.Response, log logger.Logger) *APIError {
6161
switch mimeType {
6262
case "application/json":
6363
parseJSONResponse(bodyBytes, apiError, log, resp)
64-
logError(log, apiError, "json_error_detected", resp)
6564
case "application/xml", "text/xml":
6665
parseXMLResponse(bodyBytes, apiError, log, resp)
67-
logError(log, apiError, "xml_error_detected", resp)
6866
case "text/html":
6967
parseHTMLResponse(bodyBytes, apiError, log, resp)
70-
logError(log, apiError, "html_error_detected", resp)
7168
case "text/plain":
7269
parseTextResponse(bodyBytes, apiError, log, resp)
73-
logError(log, apiError, "text_error_detected", resp)
7470
default:
7571
apiError.Raw = string(bodyBytes)
7672
apiError.Message = "Unknown content type error"
@@ -112,38 +108,41 @@ func parseJSONResponse(bodyBytes []byte, apiError *APIError, log logger.Logger,
112108
}
113109
}
114110

115-
// parseXMLResponse should be implemented to parse XML responses and log errors using the centralized logger.
111+
// parseXMLResponse dynamically parses XML error responses and accumulates potential error messages.
116112
func parseXMLResponse(bodyBytes []byte, apiError *APIError, log logger.Logger, resp *http.Response) {
117-
var xmlErr APIError
113+
// Always set the Raw field to the entire XML content for debugging purposes
114+
apiError.Raw = string(bodyBytes)
118115

119-
// Attempt to unmarshal the XML body into the XMLErrorResponse struct
120-
if err := xml.Unmarshal(bodyBytes, &xmlErr); err != nil {
121-
// If parsing fails, log the error and keep the raw response
122-
apiError.Raw = string(bodyBytes)
123-
log.LogError("xml_parsing_error",
124-
resp.Request.Method,
125-
resp.Request.URL.String(),
126-
apiError.StatusCode,
127-
fmt.Sprintf("Failed to parse XML: %s", err),
128-
err,
129-
apiError.Raw,
130-
)
131-
} else {
132-
// Update the APIError with information from the parsed XML
133-
apiError.Message = xmlErr.Message
134-
// Assuming you might want to add a 'Code' field to APIError to store xmlErr.Code
135-
// apiError.Code = xmlErr.Code
116+
// Parse the XML document
117+
doc, err := xmlquery.Parse(bytes.NewReader(bodyBytes))
118+
if err != nil {
119+
logError(log, apiError, "xml_parsing_error", resp)
120+
return
121+
}
136122

137-
// Log the parsed error details
138-
log.LogError("xml_error_detected",
139-
resp.Request.Method,
140-
resp.Request.URL.String(),
141-
apiError.StatusCode,
142-
"Parsed XML error successfully",
143-
nil, // No error during parsing
144-
apiError.Raw,
145-
)
123+
var messages []string
124+
var traverse func(*xmlquery.Node)
125+
traverse = func(n *xmlquery.Node) {
126+
if n.Type == xmlquery.TextNode && strings.TrimSpace(n.Data) != "" {
127+
messages = append(messages, strings.TrimSpace(n.Data))
128+
}
129+
for c := n.FirstChild; c != nil; c = c.NextSibling {
130+
traverse(c)
131+
}
132+
}
133+
134+
traverse(doc)
135+
136+
// Concatenate all messages found in the XML for the 'Message' field of APIError
137+
if len(messages) > 0 {
138+
apiError.Message = strings.Join(messages, "; ")
139+
} else {
140+
// Fallback error message if no specific messages were extracted
141+
apiError.Message = "Failed to extract error details from XML response"
146142
}
143+
144+
// Log the error using the centralized logger
145+
logError(log, apiError, "xml_error_detected", resp)
147146
}
148147

149148
// parseTextResponse updates the APIError structure based on a plain text error response and logs it.
@@ -157,31 +156,30 @@ func parseTextResponse(bodyBytes []byte, apiError *APIError, log logger.Logger,
157156

158157
// parseHTMLResponse extracts meaningful information from an HTML error response.
159158
func parseHTMLResponse(bodyBytes []byte, apiError *APIError, log logger.Logger, resp *http.Response) {
160-
// Convert the response body to a reader for the HTML parser
159+
// Always set the Raw field to the entire HTML content for debugging purposes
160+
apiError.Raw = string(bodyBytes)
161+
161162
reader := bytes.NewReader(bodyBytes)
162163
doc, err := html.Parse(reader)
163164
if err != nil {
164-
apiError.Raw = string(bodyBytes)
165165
logError(log, apiError, "html_parsing_error", resp)
166166
return
167167
}
168168

169169
var parse func(*html.Node)
170170
parse = func(n *html.Node) {
171-
// Look for <p> tags that might contain error messages
172171
if n.Type == html.ElementNode && n.Data == "p" {
173172
if n.FirstChild != nil {
174-
// Assuming the error message is in the text content of a <p> tag
175173
apiError.Message = n.FirstChild.Data
176-
return // Stop after finding the first <p> tag with content
174+
// Optionally, you might break or return after finding the first relevant message
177175
}
178176
}
179177
for c := n.FirstChild; c != nil; c = c.NextSibling {
180-
parse(c) // Recursively search for <p> tags in child nodes
178+
parse(c)
181179
}
182180
}
183181

184-
parse(doc) // Start parsing from the document node
182+
parse(doc)
185183

186184
// If no <p> tag was found or it was empty, fallback to using the raw HTML
187185
if apiError.Message == "" {

0 commit comments

Comments
 (0)