@@ -5,12 +5,12 @@ package response
5
5
import (
6
6
"bytes"
7
7
"encoding/json"
8
- "encoding/xml"
9
8
"fmt"
10
9
"io"
11
10
"net/http"
12
11
"strings"
13
12
13
+ "github.com/antchfx/xmlquery"
14
14
"github.com/deploymenttheory/go-api-http-client/logger"
15
15
"golang.org/x/net/html"
16
16
)
@@ -61,16 +61,12 @@ func HandleAPIErrorResponse(resp *http.Response, log logger.Logger) *APIError {
61
61
switch mimeType {
62
62
case "application/json" :
63
63
parseJSONResponse (bodyBytes , apiError , log , resp )
64
- logError (log , apiError , "json_error_detected" , resp )
65
64
case "application/xml" , "text/xml" :
66
65
parseXMLResponse (bodyBytes , apiError , log , resp )
67
- logError (log , apiError , "xml_error_detected" , resp )
68
66
case "text/html" :
69
67
parseHTMLResponse (bodyBytes , apiError , log , resp )
70
- logError (log , apiError , "html_error_detected" , resp )
71
68
case "text/plain" :
72
69
parseTextResponse (bodyBytes , apiError , log , resp )
73
- logError (log , apiError , "text_error_detected" , resp )
74
70
default :
75
71
apiError .Raw = string (bodyBytes )
76
72
apiError .Message = "Unknown content type error"
@@ -112,38 +108,41 @@ func parseJSONResponse(bodyBytes []byte, apiError *APIError, log logger.Logger,
112
108
}
113
109
}
114
110
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 .
116
112
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 )
118
115
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
+ }
136
122
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"
146
142
}
143
+
144
+ // Log the error using the centralized logger
145
+ logError (log , apiError , "xml_error_detected" , resp )
147
146
}
148
147
149
148
// 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,
157
156
158
157
// parseHTMLResponse extracts meaningful information from an HTML error response.
159
158
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
+
161
162
reader := bytes .NewReader (bodyBytes )
162
163
doc , err := html .Parse (reader )
163
164
if err != nil {
164
- apiError .Raw = string (bodyBytes )
165
165
logError (log , apiError , "html_parsing_error" , resp )
166
166
return
167
167
}
168
168
169
169
var parse func (* html.Node )
170
170
parse = func (n * html.Node ) {
171
- // Look for <p> tags that might contain error messages
172
171
if n .Type == html .ElementNode && n .Data == "p" {
173
172
if n .FirstChild != nil {
174
- // Assuming the error message is in the text content of a <p> tag
175
173
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
177
175
}
178
176
}
179
177
for c := n .FirstChild ; c != nil ; c = c .NextSibling {
180
- parse (c ) // Recursively search for <p> tags in child nodes
178
+ parse (c )
181
179
}
182
180
}
183
181
184
- parse (doc ) // Start parsing from the document node
182
+ parse (doc )
185
183
186
184
// If no <p> tag was found or it was empty, fallback to using the raw HTML
187
185
if apiError .Message == "" {
0 commit comments