diff --git a/src/services/api-service.js b/src/services/api-service.js index fde5356..adf9912 100644 --- a/src/services/api-service.js +++ b/src/services/api-service.js @@ -136,6 +136,71 @@ class APIService { } } + /** + * Extract the useful part of an HTTP error response body. + * Structured WMS errors carry the diagnostic in Message / InnerException.Message — + * a full JSON.stringify would drown it in WatsonBuckets / HResult noise. + * @param {*} data - Response body (object, string, or anything axios parsed) + * @returns {string|null} Truncated human-readable body, or null if empty + */ + _describeResponseBody(data) { + const MAX_BODY_LENGTH = 2000; + if (data == null || data === '') return null; + if (typeof data === 'string') return data.slice(0, MAX_BODY_LENGTH); + if (typeof data === 'object') { + const parts = []; + if (data.ClassName) parts.push(data.ClassName); + if (data.Message) parts.push(data.Message); + let inner = data.InnerException; + while (inner && inner.Message) { + // AggregateException répète souvent le même message dans InnerException + if (inner.Message !== data.Message) parts.push(`Inner: ${inner.Message}`); + inner = inner.InnerException; + } + const text = parts.length > 0 ? parts.join(' — ') : JSON.stringify(data); + return text.slice(0, MAX_BODY_LENGTH); + } + return String(data).slice(0, MAX_BODY_LENGTH); + } + + /** + * Build an enriched Error from a failed HTTP call: status, verb, full URL, + * request payload and response body. The WMS puts the real diagnostic + * (compile errors, unknown entity, ...) in the response body — without this, + * every failure reads "Request failed with status code 500". + * Never includes headers (Bearer token) — payloads passed through post/get + * carry no credentials. + * @param {Error} error - Original axios error + * @param {string} method - HTTP verb ('POST' | 'GET') + * @param {string} url - Full request URL + * @param {*} payload - Request body (POST) or query params (GET) + * @returns {Error} Enriched error (original kept in .cause, status in .status) + */ + _enrichHttpError(error, method, url, payload) { + const status = error.response?.status; + const parts = [`${method} ${url} failed${status != null ? ` (HTTP ${status})` : ''}: ${error.message}`]; + + if (payload !== undefined && payload !== null) { + let serialized; + try { + serialized = JSON.stringify(payload); + } catch { + serialized = String(payload); + } + if (serialized !== '{}') { + parts.push(`Request payload: ${serialized.slice(0, 1000)}`); + } + } + + const body = this._describeResponseBody(error.response?.data); + if (body) parts.push(`Response body: ${body}`); + + const enriched = new Error(parts.join('\n')); + enriched.status = status; + enriched.cause = error; + return enriched; + } + /** * Make a POST request to WMS API * @param {string} endpoint - API endpoint (e.g., '/QueryExecute' or '/AD/api/Workflow/GetByApplication') @@ -162,25 +227,31 @@ class APIService { return response.data; } catch (error) { - console.error(`[API] Request failed: ${error.message}`); - // If unauthorized, try refreshing token and retry once if (error.response?.status === 401) { console.error('[API] Unauthorized, refreshing token and retrying...'); await this.refreshOAuthToken(); - const retryResponse = await this.httpClient.post(url, data, { - headers: { - 'Authorization': `Bearer ${this.token}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json' - } - }); + try { + const retryResponse = await this.httpClient.post(url, data, { + headers: { + 'Authorization': `Bearer ${this.token}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); - return retryResponse.data; + return retryResponse.data; + } catch (retryError) { + const enrichedRetry = this._enrichHttpError(retryError, 'POST', url, data); + console.error(`[API] Retry after token refresh failed: ${enrichedRetry.message}`); + throw enrichedRetry; + } } - throw error; + const enriched = this._enrichHttpError(error, 'POST', url, data); + console.error(`[API] Request failed: ${enriched.message}`); + throw enriched; } } @@ -210,25 +281,31 @@ class APIService { return response.data; } catch (error) { - console.error(`[API] Request failed: ${error.message}`); - // If unauthorized, try refreshing token and retry once if (error.response?.status === 401) { console.error('[API] Unauthorized, refreshing token and retrying...'); await this.refreshOAuthToken(); - const retryResponse = await this.httpClient.get(url, { - params, - headers: { - 'Authorization': `Bearer ${this.token}`, - 'Accept': 'application/json' - } - }); + try { + const retryResponse = await this.httpClient.get(url, { + params, + headers: { + 'Authorization': `Bearer ${this.token}`, + 'Accept': 'application/json' + } + }); - return retryResponse.data; + return retryResponse.data; + } catch (retryError) { + const enrichedRetry = this._enrichHttpError(retryError, 'GET', url, params); + console.error(`[API] Retry after token refresh failed: ${enrichedRetry.message}`); + throw enrichedRetry; + } } - throw error; + const enriched = this._enrichHttpError(error, 'GET', url, params); + console.error(`[API] Request failed: ${enriched.message}`); + throw enriched; } }