From ad6567efa20254829eb3b6166011d10c244cae6a Mon Sep 17 00:00:00 2001 From: Vincent Guillet Date: Fri, 28 Nov 2025 11:33:15 +0100 Subject: [PATCH] Add PrestashopClient and PrestashopProxyController for API integration --- .../controller/PrestashopProxyController.java | 76 ++++++++ .../api/service/PrestashopClient.java | 168 ++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 api/src/main/java/fr/gameovergne/api/controller/PrestashopProxyController.java create mode 100644 api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java diff --git a/api/src/main/java/fr/gameovergne/api/controller/PrestashopProxyController.java b/api/src/main/java/fr/gameovergne/api/controller/PrestashopProxyController.java new file mode 100644 index 0000000..71f47e8 --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/controller/PrestashopProxyController.java @@ -0,0 +1,76 @@ +package fr.gameovergne.api.controller; + +import fr.gameovergne.api.service.PrestashopClient; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/ps") +public class PrestashopProxyController { + + private final PrestashopClient prestashopClient; + + public PrestashopProxyController(PrestashopClient prestashopClient) { + this.prestashopClient = prestashopClient; + } + + private String extractPath(HttpServletRequest request) { + return request.getRequestURI().replaceFirst("^/api/ps", ""); // => /products, /categories, ... + } + + // ---------- GET ---------- + @GetMapping("/**") + public ResponseEntity proxyGet(HttpServletRequest request) { + String path = extractPath(request); + String query = request.getQueryString(); + String body = prestashopClient.get(path, query); + + return ResponseEntity + .ok() + .contentType(MediaType.APPLICATION_JSON) + .body(body); + } + + // ---------- POST ---------- + @PostMapping("/**") + public ResponseEntity proxyPost(HttpServletRequest request, + @RequestBody String xmlBody) { + String path = extractPath(request); + String query = request.getQueryString(); + String body = prestashopClient.post(path, query, xmlBody); + + return ResponseEntity + .ok() + .contentType(MediaType.APPLICATION_JSON) + .body(body); + } + + // ---------- PUT ---------- + @PutMapping("/**") + public ResponseEntity proxyPut(HttpServletRequest request, + @RequestBody String xmlBody) { + String path = extractPath(request); + String query = request.getQueryString(); + String body = prestashopClient.put(path, query, xmlBody); + + return ResponseEntity + .ok() + .contentType(MediaType.APPLICATION_JSON) + .body(body); + } + + // ---------- DELETE ---------- + @DeleteMapping("/**") + public ResponseEntity proxyDelete(HttpServletRequest request) { + String path = extractPath(request); + String query = request.getQueryString(); + String body = prestashopClient.delete(path, query); + + return ResponseEntity + .ok() + .contentType(MediaType.APPLICATION_JSON) + .body(body); + } +} diff --git a/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java b/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java new file mode 100644 index 0000000..0ff2e9d --- /dev/null +++ b/api/src/main/java/fr/gameovergne/api/service/PrestashopClient.java @@ -0,0 +1,168 @@ +package fr.gameovergne.api.service; + +import org.slf4j.Logger; +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.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.List; + +@Service +public class PrestashopClient { + + private static final Logger log = LoggerFactory.getLogger(PrestashopClient.class); + + private final RestTemplate restTemplate = new RestTemplate(); + private final String baseUrl; + private final String basicAuth; // base64 SANS le "Basic " + + public PrestashopClient( + @Value("${prestashop.base-url}") String baseUrl, + @Value("${prestashop.basic-auth}") String basicAuth + ) { + this.baseUrl = baseUrl; + this.basicAuth = basicAuth; + } + + // ========== GET ========== + public String get(String path, String ignoredQuery) { + UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(baseUrl) + .path("/api") + .path(path) + .queryParam("output_format", "JSON") + .queryParam("display", "full"); + + String url = builder.build(true).toUriString(); + log.info("[PrestaShop] GET {}"); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + entity, + String.class + ); + log.info("[PrestaShop] Réponse GET {} pour {}"); + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("PrestaShop returned non-2xx status: " + + response.getStatusCode() + " for URL " + url); + } + + return response.getBody(); + } + + // ========== POST ========== + public String post(String path, String query, String xmlBody) { + UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(baseUrl) + .path("/api") + .path(path); + + if (query != null && !query.isBlank()) { + builder.query(query); // on laisse Angular décider si besoin + } + + String url = builder.build(true).toUriString(); + log.info("[PrestaShop] POST {}"); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + headers.setContentType(MediaType.APPLICATION_XML); // <-- XML obligatoire + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + HttpEntity entity = new HttpEntity<>(xmlBody, headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.POST, + entity, + String.class + ); + log.info("[PrestaShop] Réponse POST {} pour {}"); + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("PrestaShop returned non-2xx status: " + + response.getStatusCode() + " for URL " + url); + } + + return response.getBody(); + } + + // ========== PUT ========== + public String put(String path, String query, String xmlBody) { + UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(baseUrl) + .path("/api") + .path(path); + + if (query != null && !query.isBlank()) { + builder.query(query); + } + + String url = builder.build(true).toUriString(); + log.info("[PrestaShop] PUT {}"); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + headers.setContentType(MediaType.APPLICATION_XML); // <-- XML + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + + HttpEntity entity = new HttpEntity<>(xmlBody, headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.PUT, + entity, + String.class + ); + log.info("[PrestaShop] Réponse PUT {} pour {}"); + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("PrestaShop returned non-2xx status: " + + response.getStatusCode() + " for URL " + url); + } + + return response.getBody(); + } + + // ========== DELETE ========== + public String delete(String path, String query) { + UriComponentsBuilder builder = UriComponentsBuilder + .fromHttpUrl(baseUrl) + .path("/api") + .path(path); + + if (query != null && !query.isBlank()) { + builder.query(query); + } + + String url = builder.build(true).toUriString(); + log.info("[PrestaShop] DELETE {}"); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Basic " + basicAuth); + + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.DELETE, + entity, + String.class + ); + log.info("[PrestaShop] Réponse DELETE {} pour {}"); + if (!response.getStatusCode().is2xxSuccessful()) { + throw new RuntimeException("PrestaShop returned non-2xx status: " + + response.getStatusCode() + " for URL " + url); + } + + return response.getBody(); + } +} \ No newline at end of file