From d94ce06d95c268e69a7831b851db4554b0f0309f Mon Sep 17 00:00:00 2001 From: Vincent Guillet Date: Wed, 26 Nov 2025 12:14:07 +0100 Subject: [PATCH] Refactor PrestashopClient and PrestashopProxyController for improved API request handling and response management --- .../prestashop/PrestashopProxyController.java | 81 ++++--- .../service/prestashop/PrestashopClient.java | 199 ++++++++---------- 2 files changed, 144 insertions(+), 136 deletions(-) diff --git a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java index 859321c..3216eb0 100644 --- a/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java +++ b/api/src/main/java/fr/gameovergne/api/controller/prestashop/PrestashopProxyController.java @@ -16,65 +16,92 @@ public class PrestashopProxyController { this.prestashopClient = prestashopClient; } - // Utilitaire pour extraire /products, /products/446, etc. + /** + * Extrait le path Presta à partir de la requête entrante. + * Exemple : + * - requestURI = /api/ps/products/446 + * - retourne /products/446 + */ private String extractPath(HttpServletRequest request) { String fullPath = request.getRequestURI(); // ex: /api/ps/products/446 String contextPath = request.getContextPath(); // souvent "" String relative = fullPath.substring(contextPath.length()); // /api/ps/products/446 - return relative.replaceFirst("^/api/ps", ""); // => /products/446 + + // On enlève le préfixe /api/ps => /products/446 + return relative.replaceFirst("^/api/ps", ""); } + // ---------- GET ---------- + @GetMapping("/**") public ResponseEntity proxyGet(HttpServletRequest request) { String path = extractPath(request); - String query = request.getQueryString(); + String query = request.getQueryString(); // ex: display=full&filter[id_product]=123 String body = prestashopClient.get(path, query); + // On sait qu'on force output_format=JSON côté client.get(...) return ResponseEntity .ok() .contentType(MediaType.APPLICATION_JSON) .body(body); } - @PutMapping("/**") - public ResponseEntity proxyPut(HttpServletRequest request, - @RequestBody String body) { - String path = extractPath(request); - String query = request.getQueryString(); - - String responseBody = prestashopClient.put(path, query, body); - - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(responseBody); - } + // ---------- POST ---------- @PostMapping("/**") - public ResponseEntity proxyPost(HttpServletRequest request, - @RequestBody String body) { + public ResponseEntity proxyPost( + HttpServletRequest request, + @RequestBody String bodyFromFront + ) { String path = extractPath(request); String query = request.getQueryString(); - String responseBody = prestashopClient.post(path, query, body); + String body = prestashopClient.post(path, query, bodyFromFront); + + // Si Angular demande output_format=JSON dans la query, on peut renvoyer JSON, + // sinon ce sera typiquement du XML. Dans tous les cas on renvoie le raw body. + MediaType mediaType = (query != null && query.contains("output_format=JSON")) + ? MediaType.APPLICATION_JSON + : MediaType.APPLICATION_XML; return ResponseEntity .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(responseBody); + .contentType(mediaType) + .body(body); } + // ---------- PUT ---------- + + @PutMapping("/**") + public ResponseEntity proxyPut( + HttpServletRequest request, + @RequestBody String bodyFromFront + ) { + String path = extractPath(request); + String query = request.getQueryString(); + + String body = prestashopClient.put(path, query, bodyFromFront); + + MediaType mediaType = (query != null && query.contains("output_format=JSON")) + ? MediaType.APPLICATION_JSON + : MediaType.APPLICATION_XML; + + return ResponseEntity + .ok() + .contentType(mediaType) + .body(body); + } + + // ---------- DELETE ---------- + @DeleteMapping("/**") - public ResponseEntity proxyDelete(HttpServletRequest request) { + public ResponseEntity proxyDelete(HttpServletRequest request) { String path = extractPath(request); String query = request.getQueryString(); - String responseBody = prestashopClient.delete(path, query); + prestashopClient.delete(path, query); - return ResponseEntity - .ok() - .contentType(MediaType.APPLICATION_JSON) - .body(responseBody); + return ResponseEntity.noContent().build(); } } \ No newline at end of file diff --git a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java index 9b1d1af..933ecd1 100644 --- a/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java +++ b/api/src/main/java/fr/gameovergne/api/service/prestashop/PrestashopClient.java @@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; -import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -30,18 +29,45 @@ public class PrestashopClient { this.basicAuth = basicAuth; } - // ========================= - // GET : on force JSON + full - // ========================= - public String get(String path, String ignoredQuery) { + /** + * Construit l'URL finale vers PrestaShop en propageant la query d'origine. + * + * @param path ex: "/products", "/products/446" + * @param query queryString brute reçue côté API (peut être null) + * @param addDefaultFormatAndDisplay si true, ajoute output_format=JSON et display=full + */ + private String buildUrl(String path, String query, boolean addDefaultFormatAndDisplay) { UriComponentsBuilder builder = UriComponentsBuilder .fromHttpUrl(baseUrl) .path("/api") - .path(path) - .queryParam("output_format", "JSON") - .queryParam("display", "full"); + .path(path); - String url = builder.build(true).toUriString(); + // On propage TOUT ce que le front a mis (display, filter[...], etc.) + if (query != null && !query.isBlank()) { + builder.query(query); + } + + if (addDefaultFormatAndDisplay) { + // Si le front n'a pas mis output_format, on force JSON + if (query == null || !query.contains("output_format=")) { + builder.queryParam("output_format", "JSON"); + } + // Si le front n'a pas mis display, on force full + if (query == null || !query.contains("display=")) { + builder.queryParam("display", "full"); + } + } + + return builder.build(true).toUriString(); + } + + /** + * GET générique vers PrestaShop. + * - Propage la queryString Angular (display, filter[...], etc.) + * - Ajoute output_format=JSON & display=full si absents. + */ + public String get(String path, String query) { + String url = buildUrl(path, query, true); log.info("[PrestaShop] GET {}", url); @@ -59,10 +85,8 @@ public class PrestashopClient { String.class ); - log.info("[PrestaShop] Réponse GET {} pour {}", response.getStatusCode(), url); - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " + throw new RuntimeException("PrestaShop returned non-2xx status on GET: " + response.getStatusCode() + " for URL " + url); } @@ -73,79 +97,20 @@ public class PrestashopClient { } } - // ========================= - // PUT : on respecte la query du front - // ========================= - public String put(String path, String query, String body) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); - - if (query != null && !query.isBlank()) { - // on laisse Angular décider (ex: output_format=JSON) - builder.query(query); - } - - String url = builder.build(true).toUriString(); - log.info("[PrestaShop] PUT {}", url); - log.debug("[PrestaShop] PUT body = {}", body); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); - - HttpEntity entity = new HttpEntity<>(body, headers); - - try { - ResponseEntity response = restTemplate.exchange( - url, - HttpMethod.PUT, - entity, - String.class - ); - - log.info("[PrestaShop] Réponse PUT {} pour {}", response.getStatusCode(), url); - - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); - } - - return response.getBody(); - } catch (HttpStatusCodeException e) { - // Ici on log le body d'erreur de Presta ! - log.error("[PrestaShop] Erreur PUT {} status={} body={}", - url, e.getStatusCode(), e.getResponseBodyAsString(), e); - throw new RuntimeException("Erreur PUT PrestaShop", e); - } catch (RestClientException e) { - log.error("[PrestaShop] Erreur PUT {}", url, e); - throw new RuntimeException("Erreur PUT PrestaShop", e); - } - } - - // ========================= - // POST : création - // ========================= + /** + * POST générique vers PrestaShop. + * On propage la query telle quelle (si Angular met output_format=JSON, etc.). + * Le body est du XML (templates construits côté Angular). + */ public String post(String path, String query, String body) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); + String url = buildUrl(path, query, false); - if (query != null && !query.isBlank()) { - builder.query(query); - } - - String url = builder.build(true).toUriString(); log.info("[PrestaShop] POST {}", url); - log.debug("[PrestaShop] POST body = {}", body); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_XML); + headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)); HttpEntity entity = new HttpEntity<>(body, headers); @@ -157,43 +122,66 @@ public class PrestashopClient { String.class ); - log.info("[PrestaShop] Réponse POST {} pour {}", response.getStatusCode(), url); - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); + throw new RuntimeException("PrestaShop returned non-2xx status on POST: " + + response.getStatusCode() + " for URL " + url + + " - response: " + response.getBody()); } return response.getBody(); - } catch (HttpStatusCodeException e) { - log.error("[PrestaShop] Erreur POST {} status={} body={}", - url, e.getStatusCode(), e.getResponseBodyAsString(), e); - throw new RuntimeException("Erreur POST PrestaShop", e); } catch (RestClientException e) { log.error("[PrestaShop] Erreur POST {}", url, e); throw new RuntimeException("Erreur POST PrestaShop", e); } } - // ========================= - // DELETE : suppression - // ========================= - public String delete(String path, String query) { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(baseUrl) - .path("/api") - .path(path); + /** + * PUT générique vers PrestaShop. + * Le body est du XML (templates construits côté Angular). + */ + public String put(String path, String query, String body) { + String url = buildUrl(path, query, false); - if (query != null && !query.isBlank()) { - builder.query(query); + log.info("[PrestaShop] PUT {}", url); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + headers.setContentType(MediaType.APPLICATION_XML); + headers.setAccept(List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)); + + HttpEntity entity = new HttpEntity<>(body, headers); + + try { + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.PUT, + entity, + String.class + ); + + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("PrestaShop returned non-2xx status on PUT: " + + response.getStatusCode() + " for URL " + url + + " - response: " + response.getBody()); + } + + return response.getBody(); + } catch (RestClientException e) { + log.error("[PrestaShop] Erreur PUT {}", url, e); + throw new RuntimeException("Erreur PUT PrestaShop", e); } + } + + /** + * DELETE générique vers PrestaShop. + */ + public void delete(String path, String query) { + String url = buildUrl(path, query, false); - String url = builder.build(true).toUriString(); log.info("[PrestaShop] DELETE {}", url); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); HttpEntity entity = new HttpEntity<>(headers); @@ -205,18 +193,11 @@ public class PrestashopClient { String.class ); - log.info("[PrestaShop] Réponse DELETE {} pour {}", response.getStatusCode(), url); - if (!response.getStatusCode().is2xxSuccessful()) { - throw new RuntimeException("PrestaShop returned non-2xx status: " - + response.getStatusCode() + " for URL " + url); + throw new RuntimeException("PrestaShop returned non-2xx status on DELETE: " + + response.getStatusCode() + " for URL " + url + + " - response: " + response.getBody()); } - - return response.getBody(); - } catch (HttpStatusCodeException e) { - log.error("[PrestaShop] Erreur DELETE {} status={} body={}", - url, e.getStatusCode(), e.getResponseBodyAsString(), e); - throw new RuntimeException("Erreur DELETE PrestaShop", e); } catch (RestClientException e) { log.error("[PrestaShop] Erreur DELETE {}", url, e); throw new RuntimeException("Erreur DELETE PrestaShop", e);