Skip to content

Commit 28237d2

Browse files
committed
Add XML parsing for error responses
1 parent a4de42d commit 28237d2

File tree

3 files changed

+45
-29
lines changed

3 files changed

+45
-29
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: 32 additions & 29 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
)
@@ -108,38 +108,41 @@ func parseJSONResponse(bodyBytes []byte, apiError *APIError, log logger.Logger,
108108
}
109109
}
110110

111-
// 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.
112112
func parseXMLResponse(bodyBytes []byte, apiError *APIError, log logger.Logger, resp *http.Response) {
113-
var xmlErr APIError
113+
// Always set the Raw field to the entire XML content for debugging purposes
114+
apiError.Raw = string(bodyBytes)
114115

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

133-
// Log the parsed error details
134-
log.LogError("xml_error_detected",
135-
resp.Request.Method,
136-
resp.Request.URL.String(),
137-
apiError.StatusCode,
138-
"Parsed XML error successfully",
139-
nil, // No error during parsing
140-
apiError.Raw,
141-
)
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+
}
142132
}
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"
142+
}
143+
144+
// Log the error using the centralized logger
145+
logError(log, apiError, "xml_error_detected", resp)
143146
}
144147

145148
// parseTextResponse updates the APIError structure based on a plain text error response and logs it.

0 commit comments

Comments
 (0)