From 42c1e655f11e5763bca5d9ced117213074536168 Mon Sep 17 00:00:00 2001 From: Vincent Guillet Date: Wed, 3 Dec 2025 09:33:30 +0100 Subject: [PATCH] Refactor PrestashopClient to normalize base URL, enhance URL building, and improve raw query handling --- .../api/service/PrestashopClient.java | 183 +++++++++--------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java index 9d179dc..42e5534 100644 --- a/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java +++ b/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java @@ -1,3 +1,4 @@ +// package à adapter si besoin package fr.gameovergne.api.service; import lombok.extern.slf4j.Slf4j; @@ -19,166 +20,133 @@ import java.util.Map; public class PrestashopClient { private final RestClient client; - private final String baseUrl; // ex: https://shop.gameovergne.fr/api + private final String baseUrl; private final String apiKey; public PrestashopClient( @Value("${prestashop.api.base-url}") String baseUrl, @Value("${prestashop.api.key}") String apiKey ) { - // on normalise pour éviter les doubles / - if (baseUrl.endsWith("/")) { - this.baseUrl = baseUrl.substring(0, baseUrl.length() - 1); - } else { - this.baseUrl = baseUrl; - } + this.baseUrl = baseUrl; // ex: https://shop.gameovergne.fr/api this.apiKey = apiKey; - log.info("[PrestaShop] Base URL = {}", this.baseUrl); - log.info("[PrestaShop] API key length = {}", apiKey != null ? apiKey.length() : 0); + log.info("[PrestaShop] Base URL = {}", baseUrl); + log.info("[PrestaShop] API key length = {}", apiKey.length()); this.client = RestClient.builder() .defaultHeaders(headers -> { headers.set(HttpHeaders.USER_AGENT, "curl/8.10.1"); - // Accept JSON par défaut ; pour XML on surchargera dans les méthodes XML headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.ALL)); }) .build(); } - private String normalizePath(String path) { - if (path == null || path.isBlank()) return ""; - // /products -> products - return path.startsWith("/") ? path.substring(1) : path; - } - - /** - * Construit une URL complète Presta avec ws_key et params encodés UNE SEULE FOIS. - * Utilisée par toutes les méthodes "admin" (getJson/getXml/postXml/putXml/delete). - */ + // ------------------------------------------------------------------------ + // Outil interne : construit l’URL complète baseUrl + path + ?ws_key=...¶ms... + // ------------------------------------------------------------------------ private String buildUrl(String path, MultiValueMap params) { - StringBuilder fullUrl = new StringBuilder(); + StringBuilder full = new StringBuilder(); - fullUrl.append(baseUrl) - .append("/") - .append(normalizePath(path)); + // baseUrl + full.append(baseUrl); - // auth par ws_key (remplace Basic Auth) - String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8); - fullUrl.append("?ws_key=").append(encodedKey); + // path + if (path != null && !path.isBlank()) { + boolean baseEndsWithSlash = baseUrl.endsWith("/"); + boolean pathStartsWithSlash = path.startsWith("/"); + + if (baseEndsWithSlash && pathStartsWithSlash) { + full.append(path.substring(1)); + } else if (!baseEndsWithSlash && !pathStartsWithSlash) { + full.append("/").append(path); + } else { + full.append(path); + } + } + + // ws_key en premier param + full.append("?ws_key=").append(URLEncoder.encode(apiKey, StandardCharsets.UTF_8)); if (params != null && !params.isEmpty()) { for (Map.Entry> entry : params.entrySet()) { - String name = entry.getKey(); + String key = entry.getKey(); for (String value : entry.getValue()) { - fullUrl.append("&") - .append(URLEncoder.encode(name, StandardCharsets.UTF_8)) + full.append("&") + .append(URLEncoder.encode(key, StandardCharsets.UTF_8)) .append("=") .append(URLEncoder.encode(value, StandardCharsets.UTF_8)); } } } - return fullUrl.toString(); + String url = full.toString(); + log.debug("[PrestaShop] Built URL = {}", url); + return url; } - /** - * Utilisé par le contrôleur proxy : on reçoit la query string brute - * (déjà encodée, ex: display=%5Bid,name,active%5D&output_format=JSON) - * -> on NE LA RÉ-ENCODE PAS. - */ - public String getWithRawQuery(String resource, String rawQuery) { - StringBuilder fullUrl = new StringBuilder(); - - fullUrl.append(baseUrl) - .append("/") - .append(normalizePath(resource)); - - String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8); - fullUrl.append("?ws_key=").append(encodedKey); - - if (rawQuery != null && !rawQuery.isBlank()) { - fullUrl.append("&").append(rawQuery); - } - - String url = fullUrl.toString(); - log.info("[PrestaShop] RAW GET via ws_key = {}", url); - - return client.get() - .uri(URI.create(url)) - .accept(MediaType.APPLICATION_JSON, MediaType.ALL) - .retrieve() - .body(String.class); - } - - // ----------------------- - // GET JSON (utilisé PARTOUT dans PrestashopAdminService) - // ----------------------- - + // ------------------------------------------------------------------------ + // GET JSON (utilisé partout dans PrestashopAdminService) + // ------------------------------------------------------------------------ public String getJson(String path, MultiValueMap params) { String url = buildUrl(path, params); log.info("[PrestaShop] GET JSON {}", url); return client.get() .uri(URI.create(url)) - .accept(MediaType.APPLICATION_JSON, MediaType.ALL) + .accept(MediaType.APPLICATION_JSON) .retrieve() .body(String.class); } - // ----------------------- - // GET XML - // ----------------------- - + // ------------------------------------------------------------------------ + // GET XML (utilisé pour certains appels de stock, produit, etc.) + // ------------------------------------------------------------------------ public String getXml(String path, MultiValueMap params) { String url = buildUrl(path, params); log.info("[PrestaShop] GET XML {}", url); return client.get() .uri(URI.create(url)) - .accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) + .accept(MediaType.APPLICATION_XML) .retrieve() .body(String.class); } - // ----------------------- - // POST XML - // ----------------------- - + // ------------------------------------------------------------------------ + // POST XML (création catégorie/produit/etc.) + // ------------------------------------------------------------------------ public String postXml(String path, MultiValueMap params, String xmlBody) { String url = buildUrl(path, params); - log.info("[PrestaShop] POST XML {}", url); + log.info("[PrestaShop] POST XML {} (body length={})", url, xmlBody != null ? xmlBody.length() : 0); return client.post() .uri(URI.create(url)) .contentType(MediaType.APPLICATION_XML) - .accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) - .body(xmlBody) + .accept(MediaType.APPLICATION_XML) + .body(xmlBody != null ? xmlBody : "") .retrieve() .body(String.class); } - // ----------------------- - // PUT XML - // ----------------------- - + // ------------------------------------------------------------------------ + // PUT XML (update catégorie/produit/stock/etc.) + // ------------------------------------------------------------------------ public String putXml(String path, MultiValueMap params, String xmlBody) { String url = buildUrl(path, params); - log.info("[PrestaShop] PUT XML {}", url); + log.info("[PrestaShop] PUT XML {} (body length={})", url, xmlBody != null ? xmlBody.length() : 0); return client.put() .uri(URI.create(url)) .contentType(MediaType.APPLICATION_XML) - .accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) - .body(xmlBody) + .accept(MediaType.APPLICATION_XML) + .body(xmlBody != null ? xmlBody : "") .retrieve() .body(String.class); } - // ----------------------- - // DELETE - // ----------------------- - + // ------------------------------------------------------------------------ + // DELETE (suppression ressource) + // ------------------------------------------------------------------------ public void delete(String path, MultiValueMap params) { String url = buildUrl(path, params); log.info("[PrestaShop] DELETE {}", url); @@ -186,6 +154,43 @@ public class PrestashopClient { client.delete() .uri(URI.create(url)) .retrieve() - .body(Void.class); + .toBodilessEntity(); + } + + // ------------------------------------------------------------------------ + // Spécial proxy GET brut : on garde la query telle quelle (déjà encodée) + // ------------------------------------------------------------------------ + public String getWithRawQuery(String resource, String rawQuery) { + try { + StringBuilder fullUrl = new StringBuilder(); + fullUrl.append(baseUrl); + // baseUrl : https://shop.gameovergne.fr/api + if (!baseUrl.endsWith("/")) { + fullUrl.append("/"); + } + // resource : "categories", "products", ... + fullUrl.append(resource); + + // ws_key + String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8); + fullUrl.append("?ws_key=").append(encodedKey); + + // rawQuery = "display=%5Bid,name,active%5D&output_format=JSON" + if (rawQuery != null && !rawQuery.isBlank()) { + fullUrl.append("&").append(rawQuery); // surtout ne pas réencoder + } + + String urlString = fullUrl.toString(); + log.info("[PrestaShop] RAW GET via ws_key = {}", urlString); + + return client.get() + .uri(URI.create(urlString)) + .retrieve() + .body(String.class); + + } catch (Exception e) { + log.error("[PrestaShop] getWithRawQuery error for resource={} rawQuery={}", resource, rawQuery, e); + throw e; + } } } \ No newline at end of file