Refactor PrestashopClient to normalize base URL, enhance URL building, and improve raw query handling
This commit is contained in:
@@ -1,88 +1,191 @@
|
||||
package fr.gameovergne.api.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class PrestashopClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class);
|
||||
|
||||
private final RestClient client;
|
||||
private final String baseUrl;
|
||||
private final String baseUrl; // ex: https://shop.gameovergne.fr/api
|
||||
private final String apiKey;
|
||||
|
||||
public PrestashopClient(
|
||||
@Value("${prestashop.api.base-url}") String baseUrl,
|
||||
@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;
|
||||
|
||||
log.info("[PrestaShop] Base URL = {}", baseUrl);
|
||||
log.info("[PrestaShop] API key length = {}", apiKey.length());
|
||||
log.info("[PrestaShop] Base URL = {}", this.baseUrl);
|
||||
log.info("[PrestaShop] API key length = {}", apiKey != null ? apiKey.length() : 0);
|
||||
|
||||
this.client = RestClient.builder()
|
||||
.defaultHeaders(headers -> {
|
||||
// PLUS de Basic Auth => on force l’auth par ws_key
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
try {
|
||||
// resource = "categories", "products", ...
|
||||
StringBuilder fullUrl = new StringBuilder();
|
||||
fullUrl.append(baseUrl);
|
||||
if (!baseUrl.endsWith("/")) {
|
||||
fullUrl.append("/");
|
||||
}
|
||||
fullUrl.append(resource);
|
||||
StringBuilder fullUrl = new StringBuilder();
|
||||
|
||||
// On construit la query à la main, sans que Spring y touche
|
||||
String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8);
|
||||
fullUrl.append("?ws_key=").append(encodedKey);
|
||||
fullUrl.append(baseUrl)
|
||||
.append("/")
|
||||
.append(normalizePath(resource));
|
||||
|
||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
||||
// ATTENTION : rawQuery vient de request.getQueryString() donc déjà encodée.
|
||||
// On l’ajoute telle quelle pour ne pas casser les [%5B ... %5D]
|
||||
fullUrl.append("&").append(rawQuery);
|
||||
}
|
||||
String encodedKey = URLEncoder.encode(apiKey, StandardCharsets.UTF_8);
|
||||
fullUrl.append("?ws_key=").append(encodedKey);
|
||||
|
||||
String urlString = fullUrl.toString();
|
||||
log.info("[PrestaShop] RAW GET via ws_key = {}", urlString);
|
||||
|
||||
return client.get()
|
||||
.uri(URI.create(urlString)) // IMPORTANT : on donne l’URI complète => pas de ré-encodage
|
||||
.retrieve()
|
||||
.body(String.class);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[PrestaShop] getWithRawQuery error for resource={} rawQuery={}", resource, rawQuery, e);
|
||||
throw e;
|
||||
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);
|
||||
}
|
||||
|
||||
// (Optionnel) pour tes autres méthodes listSimple/listProducts,
|
||||
// tu peux aussi passer par ws_key plutôt que BasicAuth, même principe :
|
||||
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
|
||||
return getWithRawQuery(resource, rawQuery);
|
||||
// -----------------------
|
||||
// GET JSON (utilisé PARTOUT dans PrestashopAdminService)
|
||||
// -----------------------
|
||||
|
||||
public String getJson(String path, MultiValueMap<String, String> 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)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user