@@ -5,12 +5,12 @@ package response
55import (
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 .
116112func 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.
159158func 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