Refactor PrestashopClient to normalize base URL, enhance URL building, and improve raw query handling

This commit is contained in:
Vincent Guillet
2025-12-03 09:33:30 +01:00
parent fdb6c40bb9
commit 42c1e655f1

View File

@@ -1,3 +1,4 @@
// package à adapter si besoin
package fr.gameovergne.api.service; package fr.gameovergne.api.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -19,166 +20,133 @@ import java.util.Map;
public class PrestashopClient { public class PrestashopClient {
private final RestClient client; private final RestClient client;
private final String baseUrl; // ex: https://shop.gameovergne.fr/api private final String baseUrl;
private final String apiKey; private final String apiKey;
public PrestashopClient( public PrestashopClient(
@Value("${prestashop.api.base-url}") String baseUrl, @Value("${prestashop.api.base-url}") String baseUrl,
@Value("${prestashop.api.key}") String apiKey @Value("${prestashop.api.key}") String apiKey
) { ) {
// on normalise pour éviter les doubles / this.baseUrl = baseUrl; // ex: https://shop.gameovergne.fr/api
if (baseUrl.endsWith("/")) {
this.baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
} else {
this.baseUrl = baseUrl;
}
this.apiKey = apiKey; this.apiKey = apiKey;
log.info("[PrestaShop] Base URL = {}", this.baseUrl); log.info("[PrestaShop] Base URL = {}", baseUrl);
log.info("[PrestaShop] API key length = {}", apiKey != null ? apiKey.length() : 0); log.info("[PrestaShop] API key length = {}", apiKey.length());
this.client = RestClient.builder() this.client = RestClient.builder()
.defaultHeaders(headers -> { .defaultHeaders(headers -> {
headers.set(HttpHeaders.USER_AGENT, "curl/8.10.1"); 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)); headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.ALL));
}) })
.build(); .build();
} }
private String normalizePath(String path) { // ------------------------------------------------------------------------
if (path == null || path.isBlank()) return ""; // Outil interne : construit lURL complète baseUrl + path + ?ws_key=...&params...
// /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).
*/
private String buildUrl(String path, MultiValueMap<String, String> params) { private String buildUrl(String path, MultiValueMap<String, String> params) {
StringBuilder fullUrl = new StringBuilder(); StringBuilder full = new StringBuilder();
fullUrl.append(baseUrl) // baseUrl
.append("/") full.append(baseUrl);
.append(normalizePath(path));
// auth par ws_key (remplace Basic Auth) // path
String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8); if (path != null && !path.isBlank()) {
fullUrl.append("?ws_key=").append(encodedKey); 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()) { if (params != null && !params.isEmpty()) {
for (Map.Entry<String, List<String>> entry : params.entrySet()) { for (Map.Entry<String, List<String>> entry : params.entrySet()) {
String name = entry.getKey(); String key = entry.getKey();
for (String value : entry.getValue()) { for (String value : entry.getValue()) {
fullUrl.append("&") full.append("&")
.append(URLEncoder.encode(name, StandardCharsets.UTF_8)) .append(URLEncoder.encode(key, StandardCharsets.UTF_8))
.append("=") .append("=")
.append(URLEncoder.encode(value, StandardCharsets.UTF_8)); .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 // GET JSON (utilisé partout dans PrestashopAdminService)
* (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)
// -----------------------
public String getJson(String path, MultiValueMap<String, String> params) { public String getJson(String path, MultiValueMap<String, String> params) {
String url = buildUrl(path, params); String url = buildUrl(path, params);
log.info("[PrestaShop] GET JSON {}", url); log.info("[PrestaShop] GET JSON {}", url);
return client.get() return client.get()
.uri(URI.create(url)) .uri(URI.create(url))
.accept(MediaType.APPLICATION_JSON, MediaType.ALL) .accept(MediaType.APPLICATION_JSON)
.retrieve() .retrieve()
.body(String.class); .body(String.class);
} }
// ----------------------- // ------------------------------------------------------------------------
// GET XML // GET XML (utilisé pour certains appels de stock, produit, etc.)
// ----------------------- // ------------------------------------------------------------------------
public String getXml(String path, MultiValueMap<String, String> params) { public String getXml(String path, MultiValueMap<String, String> params) {
String url = buildUrl(path, params); String url = buildUrl(path, params);
log.info("[PrestaShop] GET XML {}", url); log.info("[PrestaShop] GET XML {}", url);
return client.get() return client.get()
.uri(URI.create(url)) .uri(URI.create(url))
.accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) .accept(MediaType.APPLICATION_XML)
.retrieve() .retrieve()
.body(String.class); .body(String.class);
} }
// ----------------------- // ------------------------------------------------------------------------
// POST XML // POST XML (création catégorie/produit/etc.)
// ----------------------- // ------------------------------------------------------------------------
public String postXml(String path, MultiValueMap<String, String> params, String xmlBody) { public String postXml(String path, MultiValueMap<String, String> params, String xmlBody) {
String url = buildUrl(path, params); 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() return client.post()
.uri(URI.create(url)) .uri(URI.create(url))
.contentType(MediaType.APPLICATION_XML) .contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) .accept(MediaType.APPLICATION_XML)
.body(xmlBody) .body(xmlBody != null ? xmlBody : "")
.retrieve() .retrieve()
.body(String.class); .body(String.class);
} }
// ----------------------- // ------------------------------------------------------------------------
// PUT XML // PUT XML (update catégorie/produit/stock/etc.)
// ----------------------- // ------------------------------------------------------------------------
public String putXml(String path, MultiValueMap<String, String> params, String xmlBody) { public String putXml(String path, MultiValueMap<String, String> params, String xmlBody) {
String url = buildUrl(path, params); 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() return client.put()
.uri(URI.create(url)) .uri(URI.create(url))
.contentType(MediaType.APPLICATION_XML) .contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL) .accept(MediaType.APPLICATION_XML)
.body(xmlBody) .body(xmlBody != null ? xmlBody : "")
.retrieve() .retrieve()
.body(String.class); .body(String.class);
} }
// ----------------------- // ------------------------------------------------------------------------
// DELETE // DELETE (suppression ressource)
// ----------------------- // ------------------------------------------------------------------------
public void delete(String path, MultiValueMap<String, String> params) { public void delete(String path, MultiValueMap<String, String> params) {
String url = buildUrl(path, params); String url = buildUrl(path, params);
log.info("[PrestaShop] DELETE {}", url); log.info("[PrestaShop] DELETE {}", url);
@@ -186,6 +154,43 @@ public class PrestashopClient {
client.delete() client.delete()
.uri(URI.create(url)) .uri(URI.create(url))
.retrieve() .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;
}
} }
} }