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;
|
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 l’auth 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 l’ajoute 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 l’URI 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user