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

This commit is contained in:
Vincent Guillet
2025-12-02 17:48:27 +01:00
parent 72f3791616
commit fdb6c40bb9

View File

@@ -1,88 +1,191 @@
package fr.gameovergne.api.service; package fr.gameovergne.api.service;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map;
@Service @Service
@Slf4j
public class PrestashopClient { public class PrestashopClient {
private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class);
private final RestClient client; private final RestClient client;
private final String baseUrl; private final String baseUrl; // ex: https://shop.gameovergne.fr/api
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
) { ) {
this.baseUrl = baseUrl; // ex: https://shop.gameovergne.fr/api // on normalise pour éviter les doubles /
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 = {}", baseUrl); log.info("[PrestaShop] Base URL = {}", this.baseUrl);
log.info("[PrestaShop] API key length = {}", apiKey.length()); log.info("[PrestaShop] API key length = {}", apiKey != null ? apiKey.length() : 0);
this.client = RestClient.builder() this.client = RestClient.builder()
.defaultHeaders(headers -> { .defaultHeaders(headers -> {
// PLUS de Basic Auth => on force lauth par ws_key
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 "";
// /products -> products
return path.startsWith("/") ? path.substring(1) : path;
}
/** /**
* Appel "proxy" brut : on reçoit la query string telle quelle et on injecte ws_key proprement. * 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) {
StringBuilder fullUrl = new StringBuilder();
fullUrl.append(baseUrl)
.append("/")
.append(normalizePath(path));
// auth par ws_key (remplace Basic Auth)
String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8);
fullUrl.append("?ws_key=").append(encodedKey);
if (params != null && !params.isEmpty()) {
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
String name = entry.getKey();
for (String value : entry.getValue()) {
fullUrl.append("&")
.append(URLEncoder.encode(name, StandardCharsets.UTF_8))
.append("=")
.append(URLEncoder.encode(value, StandardCharsets.UTF_8));
}
}
}
return fullUrl.toString();
}
/**
* 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) { public String getWithRawQuery(String resource, String rawQuery) {
try {
// resource = "categories", "products", ...
StringBuilder fullUrl = new StringBuilder(); StringBuilder fullUrl = new StringBuilder();
fullUrl.append(baseUrl);
if (!baseUrl.endsWith("/")) {
fullUrl.append("/");
}
fullUrl.append(resource);
// On construit la query à la main, sans que Spring y touche fullUrl.append(baseUrl)
.append("/")
.append(normalizePath(resource));
String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8); String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8);
fullUrl.append("?ws_key=").append(encodedKey); fullUrl.append("?ws_key=").append(encodedKey);
if (rawQuery != null && !rawQuery.isBlank()) { if (rawQuery != null && !rawQuery.isBlank()) {
// ATTENTION : rawQuery vient de request.getQueryString() donc déjà encodée.
// On lajoute telle quelle pour ne pas casser les [%5B ... %5D]
fullUrl.append("&").append(rawQuery); fullUrl.append("&").append(rawQuery);
} }
String urlString = fullUrl.toString(); String url = fullUrl.toString();
log.info("[PrestaShop] RAW GET via ws_key = {}", urlString); log.info("[PrestaShop] RAW GET via ws_key = {}", url);
return client.get() return client.get()
.uri(URI.create(urlString)) // IMPORTANT : on donne lURI complète => pas de ré-encodage .uri(URI.create(url))
.accept(MediaType.APPLICATION_JSON, MediaType.ALL)
.retrieve() .retrieve()
.body(String.class); .body(String.class);
} catch (Exception e) {
log.error("[PrestaShop] getWithRawQuery error for resource={} rawQuery={}", resource, rawQuery, e);
throw e;
}
} }
// (Optionnel) pour tes autres méthodes listSimple/listProducts, // -----------------------
// tu peux aussi passer par ws_key plutôt que BasicAuth, même principe : // GET JSON (utilisé PARTOUT dans PrestashopAdminService)
public String getJson(String resource, String fieldsQuery) { // -----------------------
// ex fieldsQuery = "display=[id,name,active]&output_format=JSON"
String rawQuery = fieldsQuery; // tu peux garder le nom si tu veux public String getJson(String path, MultiValueMap<String, String> params) {
return getWithRawQuery(resource, rawQuery); String url = buildUrl(path, params);
log.info("[PrestaShop] GET JSON {}", url);
return client.get()
.uri(URI.create(url))
.accept(MediaType.APPLICATION_JSON, MediaType.ALL)
.retrieve()
.body(String.class);
}
// -----------------------
// GET XML
// -----------------------
public String getXml(String path, MultiValueMap<String, String> 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)
.retrieve()
.body(String.class);
}
// -----------------------
// POST XML
// -----------------------
public String postXml(String path, MultiValueMap<String, String> params, String xmlBody) {
String url = buildUrl(path, params);
log.info("[PrestaShop] POST XML {}", url);
return client.post()
.uri(URI.create(url))
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL)
.body(xmlBody)
.retrieve()
.body(String.class);
}
// -----------------------
// PUT XML
// -----------------------
public String putXml(String path, MultiValueMap<String, String> params, String xmlBody) {
String url = buildUrl(path, params);
log.info("[PrestaShop] PUT XML {}", url);
return client.put()
.uri(URI.create(url))
.contentType(MediaType.APPLICATION_XML)
.accept(MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.ALL)
.body(xmlBody)
.retrieve()
.body(String.class);
}
// -----------------------
// DELETE
// -----------------------
public void delete(String path, MultiValueMap<String, String> params) {
String url = buildUrl(path, params);
log.info("[PrestaShop] DELETE {}", url);
client.delete()
.uri(URI.create(url))
.retrieve()
.body(Void.class);
} }
} }